SKE-54 create competition component

This commit is contained in:
Przemysław Stawujak 2018-12-18 21:21:39 +01:00
parent b75d8893c6
commit d9ab9802cd
12 changed files with 469 additions and 5 deletions

View File

@ -10,6 +10,7 @@ import { RolesComponent } from 'app/roles/roles.component';
import { CategoriesListComponent } from '@app/categories-list/categories-list.component';
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';
@NgModule({
imports: [
@ -25,7 +26,8 @@ import { CompetitionDetailComponent } from '@app/competition-detail/competition-
{ path: 'about', component: AboutComponent },
{ path: 'categories-list', component: CategoriesListComponent, canActivate: [AppRouteGuard] },
{ path: 'categories-list/:id', component: CompetitionsListComponent, canActivate: [AppRouteGuard] },
{ path: 'competitions/:id', component: CompetitionDetailComponent, canActivate: [AppRouteGuard] }
{ path: 'categories-list/:id/competitions/:id', component: CompetitionDetailComponent, canActivate: [AppRouteGuard] },
{ path: 'competition-create', component: CompetitionCreateComponent, data: { permission: 'Pages.Create.Competition' }, canActivate: [AppRouteGuard] },
]
}
])

View File

@ -35,13 +35,18 @@ import { RightSideBarComponent } from '@app/layout/right-sidebar.component';
import { CategoriesListComponent } from '@app/categories-list/categories-list.component';
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 { FilterClassesPipe } from '@app/pipe/filter-classes.pipe';
import {
MatSelectModule,
MatOptionModule,
MatFormFieldModule
MatFormFieldModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatIconModule
} from '@angular/material';
@NgModule({
@ -67,6 +72,7 @@ import {
CategoriesListComponent,
CompetitionsListComponent,
CompetitionDetailComponent,
CompetitionCreateComponent,
FilterClassesPipe
],
@ -84,6 +90,10 @@ import {
MatSelectModule,
MatOptionModule,
MatFormFieldModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatIconModule
],
providers: [

View File

@ -0,0 +1,12 @@
#create-competition-area {
overflow-x: hidden;
}
.title {
color: cornflowerblue;
text-shadow: 1px 1px 1px #ab93ab;
}
.element-size {
width: 500px;
}

View File

@ -0,0 +1,183 @@
<div id="create-competition-area">
<div class="container">
<h2 class="title">Tworzenie konkursu</h2>
<form (ngSubmit)="saveCompetition()"
#createCompetitionForm="ngForm">
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<input matInput
name="name"
#name="ngModel"
[(ngModel)]="createCompetition.name"
type="text"
placeholder="Nazwa konkursu"
required
maxlength="100">
<mat-error *ngIf="name.invalid && name.errors.required">
Nazwa jest wymagana.
</mat-error>
<mat-error *ngIf="name.invalid && name.errors.maxlength">
Przekroczono maksymalną długość nazwy, 100 znaków.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<mat-select placeholder="Kategorie"
[(ngModel)]="createCompetition.categoriesId"
name="categoriesId"
#categoriesId="ngModel"
multiple
required>
<mat-option *ngFor="let category of categoriesList" [value]="category.id">
{{category.name}}
</mat-option>
</mat-select>
<mat-error *ngIf="categoriesId.invalid && categoriesId.errors.required">
Kategoria jest wymagana.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<textarea matInput
matTextareaAutosize
matAutosizeMinRows="3"
matAutosizeMaxRows="6"
name="description"
#description="ngModel"
[(ngModel)]="createCompetition.description"
type="text"
placeholder="Opis"
required
maxlength="1000">
</textarea>
<mat-error *ngIf="description.invalid && description.errors.required">
Opis jest wymagany.
</mat-error>
<mat-error *ngIf="description.invalid && description.errors.maxlength">
Przekroczono maksymalną długość opisu, 1000 znaków.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<textarea matInput
matTextareaAutosize
matAutosizeMinRows="3"
matAutosizeMaxRows="6"
name="prize"
#prize="ngModel"
[(ngModel)]="createCompetition.prize"
type="text"
placeholder="Nagrody"
required
maxlength="1000">
</textarea>
<mat-error *ngIf="prize.invalid && prize.errors.required">
Opis nagród jest wymagany.
</mat-error>
<mat-error *ngIf="prize.invalid && prize.errors.maxlength">
Przekroczono maksymalną długość opisu nagród, 1000 znaków.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<mat-select placeholder="Minimalna klasa"
[(ngModel)]="createCompetition.minClass"
name="minClass"
#minClass="ngModel"
required>
<mat-option *ngFor="let possibleClass of possibleClasses" [value]="possibleClass.value">
{{possibleClass.viewValue}}
</mat-option>
</mat-select>
<mat-error *ngIf="minClass.invalid && minClass.errors.required">
Minimalna klasa jest wymagana.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<mat-select placeholder="Maksymalna klasa"
[(ngModel)]="createCompetition.maxClass"
name="maxClass"
#maxClass="ngModel"
required>
<mat-option *ngFor="let possibleClass of possibleClasses" [value]="possibleClass.value">
{{possibleClass.viewValue}}
</mat-option>
</mat-select>
<mat-error *ngIf="maxClass.invalid && maxClass.errors.required">
Maksymalna klasa jest wymagana.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<input matInput
[matDatepicker]="picker"
placeholder="Wybierz datę rozpoczęcia"
[(ngModel)]="createCompetition.startDate"
name="startDate"
#startDate="ngModel"
disabled
min="{{ today | date: 'yyyy-MM-dd'}}"
max="{{ createCompetition.endDate | date: 'yyyy-MM-dd'}}">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker disabled="false"></mat-datepicker>
<mat-error *ngIf="startDate.invalid && startDate.errors">
Data rozpoczęcia konkursu jest nieprawidłowa.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<mat-form-field class="element-size">
<input matInput
[matDatepicker]="picker2"
placeholder="Wybierz datę zakończenia"
[(ngModel)]="createCompetition.endDate"
name="endDate"
#endDate="ngModel"
disabled
min="{{ createCompetition.startDate | date: 'yyyy-MM-dd'}}">
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2 disabled="false"></mat-datepicker>
<mat-error *ngIf="endDate.invalid && endDate.errors">
Data zakończenia konkursu jest nieprawidłowa.
</mat-error>
</mat-form-field>
</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>

View File

@ -0,0 +1,92 @@
import { Component, OnInit, Injector, OnDestroy } from '@angular/core';
import { appModuleAnimation } from '@shared/animations/routerTransition';
import { CategoryServiceProxy, CategoryDto, CompetitionServiceProxy, CreateCompetitionDto } from '@shared/service-proxies/service-proxies';
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';
@Component({
templateUrl: './competition-create.component.html',
styleUrls: ['./competition-create.component.css'],
animations: [appModuleAnimation()]
})
export class CompetitionCreateComponent extends AppComponentBase implements OnInit, OnDestroy {
public categoriesList: List<CategoryDto> = [];
public createCompetitionAreaId: string = 'create-competition-area';
public createCompetition: CreateCompetitionDto = null;;
private categoryListSubscription: Subscription;
private createCompetitionSubscription: Subscription;
public today: Date = new Date();
public possibleClasses = [
{value: 1, viewValue: 'Klasa 1'},
{value: 2, viewValue: 'Klasa 2'},
{value: 3, viewValue: 'Klasa 3'},
{value: 4, viewValue: 'Klasa 4'},
{value: 5, viewValue: 'Klasa 5'},
{value: 6, viewValue: 'Klasa 6'},
{value: 7, viewValue: 'Klasa 7'},
{value: 8, viewValue: 'Klasa 8'}
];
constructor(
injector: Injector,
private _categoryService: CategoryServiceProxy,
private _competitionService: CompetitionServiceProxy,
private router: Router
) {
super(injector);
}
public ngOnInit() {
this.createCompetition = new CreateCompetitionDto();
this.getCompetitionCategories();
}
public ngOnDestroy() {
if (this.categoryListSubscription) {
this.categoryListSubscription.unsubscribe();
}
if (this.createCompetitionSubscription) {
this.createCompetitionSubscription.unsubscribe();
}
}
private getCompetitionCategories(): void {
this.setBusy(this.createCompetitionAreaId);
this.categoryListSubscription = this._categoryService.getAllCategories()
.pipe(finalize(() => { this.clearBusy(this.createCompetitionAreaId); }))
.subscribe((result: List<CategoryDto>) => {
this.categoriesList = result;
});
}
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');
this.createCompetitionSubscription = this._competitionService.createCompetition(this.createCompetition)
.subscribe((result: number) => {
this.goToCategoriesList();
this.notify.success(this.l('Zapisano pomyślnie'));
});
}
public goToCategoriesList(): void {
const route: string = `app/categories-list`;
this.router.navigate([route]);
}
}

View File

@ -72,9 +72,9 @@ export class CompetitionsListComponent extends AppComponentBase implements OnIni
}
public goToDetail(competition: CompetitionDto): void {
//console.log(this.router.url);
//const route: string = this.router.url + `/${competition.id}`;
const route: string = `app/competitions/${competition.id}`;
console.log(this.router.url);
const route: string = this.router.url + `/competitions/${competition.id}`;
//const route: string = `app/competitions/${competition.id}`;
this.router.navigate([route]);
}

View File

@ -13,6 +13,7 @@ export class SideBarNavComponent extends AppComponentBase {
new MenuItem(this.l("Strona domowa"), "", "home", "/app/home"),
new MenuItem(this.l("Konkursy"), "", "list", "/app/categories-list"),
new MenuItem(this.l("Dodaj konkurs"), "Pages.Create.Competition", "add", "/app/competition-create"),
new MenuItem(this.l("Tenants"), "Pages.Tenants", "business", "/app/tenants"),
new MenuItem(this.l("Użytkownicy"), "Pages.Users", "people", "/app/users"),

View File

@ -207,6 +207,74 @@ export class CategoryServiceProxy {
}
}
@Injectable()
export class CompetitionServiceProxy {
private http: HttpClient;
private baseUrl: string;
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
constructor(@Inject(HttpClient) http: HttpClient, @Optional() @Inject(API_BASE_URL) baseUrl?: string) {
this.http = http;
this.baseUrl = baseUrl ? baseUrl : "";
}
/**
* @createCompetitionDto (optional)
* @return Success
*/
createCompetition(createCompetitionDto: CreateCompetitionDto | null | undefined): Observable<number> {
let url_ = this.baseUrl + "/api/services/app/Competition/CreateCompetition";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(createCompetitionDto);
let options_ : any = {
body: content_,
observe: "response",
responseType: "blob",
headers: new HttpHeaders({
"Content-Type": "application/json",
"Accept": "application/json"
})
};
return this.http.request("post", url_, options_).pipe(_observableMergeMap((response_ : any) => {
return this.processCreateCompetition(response_);
})).pipe(_observableCatch((response_: any) => {
if (response_ instanceof HttpResponseBase) {
try {
return this.processCreateCompetition(<any>response_);
} catch (e) {
return <Observable<number>><any>_observableThrow(e);
}
} else
return <Observable<number>><any>_observableThrow(response_);
}));
}
protected processCreateCompetition(response: HttpResponseBase): Observable<number> {
const status = response.status;
const responseBlob =
response instanceof HttpResponse ? response.body :
(<any>response).error instanceof Blob ? (<any>response).error : undefined;
let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }};
if (status === 200) {
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 !== undefined ? resultData200 : <any>null;
return _observableOf(result200);
}));
} else if (status !== 200 && status !== 204) {
return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
}));
}
return _observableOf<number>(<any>null);
}
}
@Injectable()
export class CompetitionCategoryServiceProxy {
private http: HttpClient;
@ -1959,6 +2027,85 @@ export interface ICategoryDto {
id: number | undefined;
}
export class CreateCompetitionDto implements ICreateCompetitionDto {
name: string | undefined;
startDate: moment.Moment | undefined;
endDate: moment.Moment | undefined;
description: string | undefined;
prize: string | undefined;
minClass: number | undefined;
maxClass: number | undefined;
categoriesId: number[] | undefined;
constructor(data?: ICreateCompetitionDto) {
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.startDate = data["startDate"] ? moment(data["startDate"].toString()) : <any>undefined;
this.endDate = data["endDate"] ? moment(data["endDate"].toString()) : <any>undefined;
this.description = data["description"];
this.prize = data["prize"];
this.minClass = data["minClass"];
this.maxClass = data["maxClass"];
if (data["categoriesId"] && data["categoriesId"].constructor === Array) {
this.categoriesId = [];
for (let item of data["categoriesId"])
this.categoriesId.push(item);
}
}
}
static fromJS(data: any): CreateCompetitionDto {
data = typeof data === 'object' ? data : {};
let result = new CreateCompetitionDto();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["name"] = this.name;
data["startDate"] = this.startDate ? this.startDate.toISOString() : <any>undefined;
data["endDate"] = this.endDate ? this.endDate.toISOString() : <any>undefined;
data["description"] = this.description;
data["prize"] = this.prize;
data["minClass"] = this.minClass;
data["maxClass"] = this.maxClass;
if (this.categoriesId && this.categoriesId.constructor === Array) {
data["categoriesId"] = [];
for (let item of this.categoriesId)
data["categoriesId"].push(item);
}
return data;
}
clone(): CreateCompetitionDto {
const json = this.toJSON();
let result = new CreateCompetitionDto();
result.init(json);
return result;
}
}
export interface ICreateCompetitionDto {
name: string | undefined;
startDate: moment.Moment | undefined;
endDate: moment.Moment | undefined;
description: string | undefined;
prize: string | undefined;
minClass: number | undefined;
maxClass: number | undefined;
categoriesId: number[] | undefined;
}
export class CompetitionDto implements ICompetitionDto {
name: string | undefined;
startDate: moment.Moment | undefined;

View File

@ -15,6 +15,7 @@ import * as ApiServiceProxies from './service-proxies';
ApiServiceProxies.ConfigurationServiceProxy,
ApiServiceProxies.CategoryServiceProxy,
ApiServiceProxies.CompetitionCategoryServiceProxy,
ApiServiceProxies.CompetitionServiceProxy,
{ provide: HTTP_INTERCEPTORS, useClass: AbpHttpInterceptor, multi: true }
]
})

View File

@ -27,6 +27,8 @@ namespace SystemKonkursow.Competition.CompetitionCategory
var mappedCompetition = ObjectMapper.Map<Domain.Competition>(createCompetitionDto);
mappedCompetition.CreatorUserId = user.Id;
mappedCompetition.StartDate = DateHelper.GetLocalTime(createCompetitionDto.StartDate);
mappedCompetition.EndDate = DateHelper.GetLocalTime(createCompetitionDto.EndDate);
var newCompetitionId = await _competitionRepository.InsertAndGetIdAsync(mappedCompetition);

View File

@ -7,6 +7,7 @@ namespace SystemKonkursow.Competition.CompetitionCategory.Dto
public CompetitionCategoryMapProfile()
{
CreateMap<Domain.CompetitionCategory, CompetitionDto>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.CompetitionId))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Competition.Name))
.ForMember(dest => dest.StartDate, opt => opt.MapFrom(src => src.Competition.StartDate))
.ForMember(dest => dest.EndDate, opt => opt.MapFrom(src => src.Competition.EndDate))

View File

@ -0,0 +1,13 @@
using System;
namespace SystemKonkursow
{
public static class DateHelper
{
public static DateTime GetLocalTime(DateTime dateTime)
{
TimeZoneInfo timeZone = TimeZoneInfo.Local;
return TimeZoneInfo.ConvertTime(dateTime, timeZone);
}
}
}