SKE-5 add questions to competition

This commit is contained in:
Przemysław Stawujak 2018-12-30 16:02:47 +01:00
parent 49f0441067
commit cca78cdcc8
15 changed files with 436 additions and 13 deletions

View File

@ -36,6 +36,7 @@ import { CategoriesListComponent } from '@app/categories-list/categories-list.co
import { CompetitionsListComponent } from '@app/competitions-list/competitions-list.component';
import { CompetitionDetailComponent } from '@app/competition-detail/competition-detail.component';
import { CompetitionCreateComponent } from '@app/competition-create/competition-create.component';
import { CreateQuestionComponent } from '@app/create-question/create-question.component';
import { FilterClassesPipe } from '@app/pipe/filter-classes.pipe';
@ -73,6 +74,7 @@ import {
CompetitionsListComponent,
CompetitionDetailComponent,
CompetitionCreateComponent,
CreateQuestionComponent,
FilterClassesPipe
],

View File

@ -20,3 +20,10 @@
width: 520px;
margin-bottom: 10px;
}
.question-name {
white-space: nowrap;
width: 400px;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -175,9 +175,21 @@
</div>
</div>
<div class="col-sm-8 col-md-8">
<button type="button" data-toggle="modal" style="margin-bottom: 20px" class="btn btn-success" (click)="createQuestion()">Dodaj pytanie</button>
</div>
<div class="col-sm-8 col-md-8">
<div *ngFor="let question of questionsList">
<label class="question-name">{{question.name}}</label>
</div>
</div>
<div class="col-sm-8 col-md-8">
<button type="submit" style="margin-bottom: 20px" class="btn btn-success" [disabled]="!createCompetitionForm.form.valid">Utwórz</button>
</div>
</form>
</div>
</div>
</div>
<create-question-modal #createQuestionModal (modalSave)="addQuestion($event)"></create-question-modal>

View File

@ -1,12 +1,18 @@
import { Component, OnInit, Injector, OnDestroy } from '@angular/core';
import { Component, OnInit, Injector, OnDestroy, ViewChild } from '@angular/core';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import { CategoryServiceProxy, CategoryDto, CompetitionServiceProxy, CreateCompetitionDto } from '@shared/service-proxies/service-proxies';
import { CategoryServiceProxy,
CategoryDto,
CompetitionServiceProxy,
CreateCompetitionDto,
CreateQuestionDto } from '@shared/service-proxies/service-proxies';
import { CreateQuestionComponent } from 'app/create-question/create-question.component';
import { List } from 'lodash';
import { AppComponentBase } from '@shared/app-component-base';
import { Subscription } from 'rxjs/Rx';
import { finalize } from 'rxjs/operators';
import { Router } from '@angular/router';
import * as moment from 'moment';
import swal from 'sweetalert';
@Component({
templateUrl: './competition-create.component.html',
@ -15,10 +21,14 @@ import * as moment from 'moment';
})
export class CompetitionCreateComponent extends AppComponentBase implements OnInit, OnDestroy {
@ViewChild('createQuestionModal') createQuestionModal: CreateQuestionComponent;
public categoriesList: List<CategoryDto> = [];
public createCompetitionAreaId: string = 'create-competition-area';
public createCompetition: CreateCompetitionDto = null;;
public createCompetition: CreateCompetitionDto = null;
public questionsList: CreateQuestionDto[] = null;
private categoryListSubscription: Subscription;
private createCompetitionSubscription: Subscription;
@ -47,6 +57,7 @@ export class CompetitionCreateComponent extends AppComponentBase implements OnIn
public ngOnInit() {
this.createCompetition = new CreateCompetitionDto();
this.questionsList = [];
this.getCompetitionCategories();
}
@ -72,17 +83,41 @@ export class CompetitionCreateComponent extends AppComponentBase implements OnIn
public saveCompetition(): void {
console.log('Save competition');
console.log(this.createCompetition);
this.createCompetition.startDate = moment(this.createCompetition.startDate, 'YYYY-MM-DD');
this.createCompetition.endDate = moment(this.createCompetition.endDate, 'YYYY-MM-DD');
if ((this.createCompetition.maxClass < this.createCompetition.minClass)
|| (!this.createCompetition.startDate)
|| (!this.createCompetition.endDate)
|| (this.questionsList.length === 0)) {
this.createCompetitionSubscription = this._competitionService.createCompetition(this.createCompetition)
.subscribe((result: number) => {
this.goToCategoriesList();
this.notify.success(this.l('Zapisano pomyślnie'));
});
swal({
title: "Błąd - Dodanie konkursu!",
text: "Sprawdź, czy wybrałeś daty konkursu. Maksymalna klasa musi być większa niż minimalna klasa. Konkurs musi mieć chociaż jedno pytanie.",
icon: "error"
})
} else {
this.createCompetition.startDate = moment(this.createCompetition.startDate, 'YYYY-MM-DD');
this.createCompetition.endDate = moment(this.createCompetition.endDate, 'YYYY-MM-DD');
this.createCompetition.createQuestions = this.questionsList;
this.createCompetitionSubscription = this._competitionService.createCompetition(this.createCompetition)
.subscribe((result: number) => {
this.goToCategoriesList();
this.notify.success(this.l('Zapisano pomyślnie'));
});
}
}
// Show Question Modal
public createQuestion(): void {
this.createQuestionModal.show();
}
public addQuestion(question): void {
this.questionsList.push(question);
}
public goToCategoriesList(): void {

View File

@ -0,0 +1,87 @@
<div bsModal #createQuestionModal="bs-modal" class="modal fade" (onShown)="onShown()" tabindex="-1" role="dialog" aria-labelledby="createQuestionModal" aria-hidden="true" [config]="{backdrop: 'static'}">
<div class="modal-dialog">
<div #modalContent class="modal-content">
<form *ngIf="active" #createQuestionForm="ngForm" id="frm_create_question" novalidate (ngSubmit)="save()">
<div class="modal-header">
<button type="button" class="close" (click)="close()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
<span>{{l("Nowe pytanie")}}</span>
</h4>
</div>
<div class="modal-body">
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="questionname" type="text" name="QuestionName" [(ngModel)]="question.name" required class="validate form-control">
<label for="questionname" class="form-label">{{l("Nazwa pytania")}}</label>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="correctOption" type="text" name="CorrectOption" [(ngModel)]="question.correctOption" required class="validate form-control">
<label for="correctOption" class="form-label">{{l("Poprawna odpowiedź")}}</label>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="firstIncorrectOption" type="text" name="FirstIncorrectOption" [(ngModel)]="question.firstIncorrectOption" required class="validate form-control">
<label for="firstIncorrectOption" class="form-label">{{l("Błędna odpowiedź")}}</label>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="secondIncorrectOption" type="text" name="SecondIncorrectOption" [(ngModel)]="question.secondIncorrectOption" required class="validate form-control">
<label for="secondIncorrectOption" class="form-label">{{l("Błędna odpowiedź")}}</label>
</div>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="thirdIncorrectOption" type="text" name="ThirdIncorrectOption" [(ngModel)]="question.thirdIncorrectOption" required class="validate form-control">
<label for="thirdIncorrectOption" class="form-label">{{l("Błędna odpowiedź")}}</label>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button [disabled]="saving" type="button" class="btn btn-default waves-effect" (click)="close()">
{{l("Anuluj")}}
</button>
<button [disabled]="!createQuestionForm.form.valid || saving" type="submit" class="btn btn-primary waves-effect">
{{l("Zapisz")}}
</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,50 @@
import { Component, ViewChild, Injector, Output, EventEmitter, ElementRef, OnInit } from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap';
import { CreateQuestionDto } from '@shared/service-proxies/service-proxies';
import { AppComponentBase } from '@shared/app-component-base';
@Component({
selector: 'create-question-modal',
templateUrl: './create-question.component.html'
})
export class CreateQuestionComponent extends AppComponentBase implements OnInit {
@ViewChild('createQuestionModal') modal: ModalDirective;
@ViewChild('modalContent') modalContent: ElementRef;
@Output() modalSave: EventEmitter<CreateQuestionDto> = new EventEmitter<CreateQuestionDto>();
active: boolean = false;
saving: boolean = false;
question: CreateQuestionDto = null;
constructor(
injector: Injector,
) {
super(injector);
}
ngOnInit(): void {
}
show(): void {
this.active = true;
this.modal.show();
this.question = new CreateQuestionDto();
}
onShown(): void {
$.AdminBSB.input.activate($(this.modalContent.nativeElement));
}
public save(): void {
this.notify.info(this.l('Pytanie dodano pomyślnie'));
this.close();
this.modalSave.emit(this.question);
}
public close(): void {
this.active = false;
this.modal.hide();
}
}

View File

@ -2044,6 +2044,7 @@ export class CreateCompetitionDto implements ICreateCompetitionDto {
minClass: number | undefined;
maxClass: number | undefined;
categoriesId: number[] | undefined;
createQuestions: CreateQuestionDto[] | undefined;
constructor(data?: ICreateCompetitionDto) {
if (data) {
@ -2068,6 +2069,11 @@ export class CreateCompetitionDto implements ICreateCompetitionDto {
for (let item of data["categoriesId"])
this.categoriesId.push(item);
}
if (data["createQuestions"] && data["createQuestions"].constructor === Array) {
this.createQuestions = [];
for (let item of data["createQuestions"])
this.createQuestions.push(CreateQuestionDto.fromJS(item));
}
}
}
@ -2092,6 +2098,11 @@ export class CreateCompetitionDto implements ICreateCompetitionDto {
for (let item of this.categoriesId)
data["categoriesId"].push(item);
}
if (this.createQuestions && this.createQuestions.constructor === Array) {
data["createQuestions"] = [];
for (let item of this.createQuestions)
data["createQuestions"].push(item.toJSON());
}
return data;
}
@ -2112,6 +2123,66 @@ export interface ICreateCompetitionDto {
minClass: number | undefined;
maxClass: number | undefined;
categoriesId: number[] | undefined;
createQuestions: CreateQuestionDto[] | undefined;
}
export class CreateQuestionDto implements ICreateQuestionDto {
name: string | undefined;
correctOption: string | undefined;
firstIncorrectOption: string | undefined;
secondIncorrectOption: string | undefined;
thirdIncorrectOption: string | undefined;
constructor(data?: ICreateQuestionDto) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
}
init(data?: any) {
if (data) {
this.name = data["name"];
this.correctOption = data["correctOption"];
this.firstIncorrectOption = data["firstIncorrectOption"];
this.secondIncorrectOption = data["secondIncorrectOption"];
this.thirdIncorrectOption = data["thirdIncorrectOption"];
}
}
static fromJS(data: any): CreateQuestionDto {
data = typeof data === 'object' ? data : {};
let result = new CreateQuestionDto();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["name"] = this.name;
data["correctOption"] = this.correctOption;
data["firstIncorrectOption"] = this.firstIncorrectOption;
data["secondIncorrectOption"] = this.secondIncorrectOption;
data["thirdIncorrectOption"] = this.thirdIncorrectOption;
return data;
}
clone(): CreateQuestionDto {
const json = this.toJSON();
let result = new CreateQuestionDto();
result.init(json);
return result;
}
}
export interface ICreateQuestionDto {
name: string | undefined;
correctOption: string | undefined;
firstIncorrectOption: string | undefined;
secondIncorrectOption: string | undefined;
thirdIncorrectOption: string | undefined;
}
export class CompetitionDto implements ICompetitionDto {

View File

@ -1,6 +1,7 @@
using Abp.Authorization;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using System.Collections.Generic;
using System.Threading.Tasks;
using SystemKonkursow.Competition.CompetitionCategory.Dto;
@ -10,12 +11,18 @@ namespace SystemKonkursow.Competition.CompetitionCategory
{
private readonly IRepository<Domain.CompetitionCategory, int> _competitionCategoryRepository;
private readonly IRepository<Domain.Competition, long> _competitionRepository;
private readonly IRepository<Domain.Question, int> _questionRepository;
private readonly IRepository<Domain.QuestionOption, int> _questionOptionRepository;
public CompetitionAppService(IRepository<Domain.CompetitionCategory, int> competitionCategoryRepository,
IRepository<Domain.Competition, long> competitionRepository)
IRepository<Domain.Competition, long> competitionRepository,
IRepository<Domain.Question, int> questionRepository,
IRepository<Domain.QuestionOption, int> questionOptionRepository)
{
_competitionCategoryRepository = competitionCategoryRepository;
_competitionRepository = competitionRepository;
_questionRepository = questionRepository;
_questionOptionRepository = questionOptionRepository;
}
[AbpAuthorize]
@ -43,6 +50,45 @@ namespace SystemKonkursow.Competition.CompetitionCategory
await _competitionCategoryRepository.InsertAsync(competitionCategory);
}
foreach (CreateQuestionDto createQuestion in createCompetitionDto.CreateQuestions)
{
List<Domain.QuestionOption> questionOptions = new List<Domain.QuestionOption>
{
new Domain.QuestionOption
{
Name = createQuestion.CorrectOption,
IsAnswer = true
},
new Domain.QuestionOption
{
Name = createQuestion.FirstIncorrectOption,
IsAnswer = false
},
new Domain.QuestionOption
{
Name = createQuestion.SecondIncorrectOption,
IsAnswer = false
},
new Domain.QuestionOption
{
Name = createQuestion.ThirdIncorrectOption,
IsAnswer = false
}
};
var question = new Domain.Question
{
Name = createQuestion.Name,
CompetitionId = newCompetitionId,
QuestionOptions = questionOptions
};
await _questionRepository.InsertAsync(question);
}
return newCompetitionId;
}
}

View File

@ -20,5 +20,7 @@ namespace SystemKonkursow.Competition.CompetitionCategory.Dto
public int MaxClass { get; set; }
public List<int> CategoriesId { get; set; }
public List<CreateQuestionDto> CreateQuestions { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace SystemKonkursow.Competition.CompetitionCategory.Dto
{
public class CreateQuestionDto
{
public string Name { get; set; }
public string CorrectOption { get; set; }
public string FirstIncorrectOption { get; set; }
public string SecondIncorrectOption { get; set; }
public string ThirdIncorrectOption { get; set; }
}
}

View File

@ -1,5 +1,6 @@
using Abp.Domain.Entities.Auditing;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using SystemKonkursow.Authorization.Users;
@ -29,5 +30,7 @@ namespace SystemKonkursow.Domain
[ForeignKey(nameof(CreatorUserId))]
public virtual User Creator { get; set; }
public virtual List<Question> Questions { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using Abp.Domain.Entities;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace SystemKonkursow.Domain
{
[Table("Questions")]
public class Question : Entity<int>
{
public string Name { get; set; }
public long CompetitionId { get; set; }
[ForeignKey(nameof(CompetitionId))]
public virtual Competition Competition { get; set; }
public virtual List<QuestionOption> QuestionOptions { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using Abp.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace SystemKonkursow.Domain
{
[Table("QuestionOptions")]
public class QuestionOption : Entity<int>
{
public string Name { get; set; }
public int QuestionId { get; set; }
public bool IsAnswer { get; set; }
[ForeignKey(nameof(QuestionId))]
public virtual Question Question { get; set; }
}
}

View File

@ -24,5 +24,9 @@ namespace SystemKonkursow.EntityFrameworkCore
public DbSet<Domain.Competition> Competitions { get; set; }
public DbSet<Domain.Question> Questions { get; set; }
public DbSet<Domain.QuestionOption> QuestionOptions { get; set; }
}
}

View File

@ -1098,6 +1098,42 @@ namespace SystemKonkursow.Migrations
b.ToTable("Participants");
});
modelBuilder.Entity("SystemKonkursow.Domain.Question", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("CompetitionId");
b.Property<string>("Name");
b.HasKey("Id");
b.HasIndex("CompetitionId");
b.ToTable("Questions");
});
modelBuilder.Entity("SystemKonkursow.Domain.QuestionOption", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<bool>("IsAnswer");
b.Property<string>("Name");
b.Property<int>("QuestionId");
b.HasKey("Id");
b.HasIndex("QuestionId");
b.ToTable("QuestionOptions");
});
modelBuilder.Entity("SystemKonkursow.MultiTenancy.Tenant", b =>
{
b.Property<int>("Id")
@ -1327,6 +1363,22 @@ namespace SystemKonkursow.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SystemKonkursow.Domain.Question", b =>
{
b.HasOne("SystemKonkursow.Domain.Competition", "Competition")
.WithMany("Questions")
.HasForeignKey("CompetitionId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SystemKonkursow.Domain.QuestionOption", b =>
{
b.HasOne("SystemKonkursow.Domain.Question", "Question")
.WithMany("QuestionOptions")
.HasForeignKey("QuestionId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("SystemKonkursow.MultiTenancy.Tenant", b =>
{
b.HasOne("SystemKonkursow.Authorization.Users.User", "CreatorUser")