Add requirements list and editing, fix type issues

Added suport for numbers and dates
This commit is contained in:
Norbi Peti 2022-05-14 14:56:06 +02:00
parent 744cc29346
commit ee4fee004d
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
11 changed files with 107 additions and 32 deletions

View file

@ -15,13 +15,13 @@ export class FulfillmentModeList implements ListResponse<FulfillmentMode> {
export class FulfillmentModeCreateInput implements Omit<DataObject<FulfillmentMode>, 'requirements' | 'courseId'> {
@field()
name: string;
@field()
@field(returns => Int)
threshold2: number;
@field()
@field(returns => Int)
threshold3: number;
@field()
@field(returns => Int)
threshold4: number;
@field()
@field(returns => Int)
threshold5: number;
@field(returns => ID)
courseId: number;

View file

@ -1,6 +1,6 @@
import { ListResponse } from './list';
import { Requirement } from '../models';
import { field, inputType, Int, objectType } from '@loopback/graphql';
import { field, ID, inputType, Int, objectType } from '@loopback/graphql';
import { DataObject } from '@loopback/repository';
@objectType()
@ -12,21 +12,24 @@ export class RequirementList implements ListResponse<Requirement> {
}
@inputType()
export class RequirementCreateInput implements Pick<DataObject<Requirement>, 'name' | 'description' | 'deadline' | 'minPoints' | 'maxPoints'> {
export class RequirementCreateInput implements Pick<DataObject<Requirement>, 'name' | 'description' | 'deadline' | 'minPoints' | 'maxPoints' | 'fulfillmentModeId'> {
@field()
deadline: Date;
@field()
name: string;
@field()
description: string;
@field()
@field(returns => Int)
minPoints: number;
@field()
@field(returns => Int)
maxPoints: number;
@field(returns => ID)
fulfillmentModeId: number;
}
@inputType()
export class RequirementUpdateInput extends RequirementCreateInput {
@field(returns => Int)
@field(returns => ID)
id: number;
}

View file

@ -1,6 +1,6 @@
import { Entity, hasMany, model, property } from '@loopback/repository';
import { Requirement } from './requirement.model';
import { field, ID, objectType } from '@loopback/graphql';
import { field, ID, Int, objectType } from '@loopback/graphql';
@model()
@objectType()
@ -24,28 +24,28 @@ export class FulfillmentMode extends Entity {
type: 'number',
required: true,
})
@field()
@field(returns => Int)
threshold2: number;
@property({
type: 'number',
required: true,
})
@field()
@field(returns => Int)
threshold3: number;
@property({
type: 'number',
required: true,
})
@field()
@field(returns => Int)
threshold4: number;
@property({
type: 'number',
required: true,
})
@field()
@field(returns => Int)
threshold5: number;
@property({

View file

@ -2,10 +2,20 @@
<form [formGroup]="formGroup">
<div *ngFor="let field of fields">
<mat-label>{{ field.title }}</mat-label>
<span [ngSwitch]="getType(item[field.name])">
<span [ngSwitch]="getType(field, item[field.name])">
<span *ngSwitchCase="'boolean'">
<mat-checkbox [checked]="item[field.name]" [formControlName]="field.name"></mat-checkbox>
</span>
<span *ngSwitchCase="'integer'">
<mat-form-field>
<input matInput [formControlName]="field.name" type="number" [value]="item[field.name]"/>
</mat-form-field>
</span>
<span *ngSwitchCase="'date'">
<mat-form-field>
<mat-datepicker [formControlName]="field.name" [ngModel]="item[field.name]"></mat-datepicker>
</mat-form-field>
</span>
<mat-form-field *ngSwitchDefault>
<input matInput [formControlName]="field.name" type="text" [value]="item[field.name]"/>
</mat-form-field>

View file

@ -15,13 +15,13 @@ export class EditComponent<T extends HasID, QT extends QueryResult<T>, UT extend
implements OnInit {
item?: T;
creating = false;
@Input() creating = false;
isLoading = true;
@Input() gql: Query<QT, HasID>;
@Input() updateMutation: Mutation<UT, MutationInput<MIU, T>>;
@Input() createMutation: Mutation<CT, MutationInput<MIC, T>>;
@Input() fields: { title: string, name: keyof T, readonly?: (item: T) => boolean }[];
@Input() fields: { title: string, name: keyof T, readonly?: (item: T) => boolean, type?: string }[];
@Input() itemType: T;
/**
* Beküldés előtt extra adat hozzáadása
@ -42,7 +42,7 @@ export class EditComponent<T extends HasID, QT extends QueryResult<T>, UT extend
const url = this.route.snapshot.url;
this.item = this.customItem;
this.id = this.item?.id ?? this.gql ? url[url.length - 1].path : 'new';
if (!this.item && this.id !== 'new' && this.gql) {
if (!this.item && this.id !== 'new' && this.gql && !this.creating) {
const data = (await this.gql.fetch({id: this.id}).toPromise()).data;
this.key = Object.keys(data).filter(k => k !== '__typename')[0];
this.item = data[this.key];
@ -87,8 +87,8 @@ export class EditComponent<T extends HasID, QT extends QueryResult<T>, UT extend
await this.router.navigate(['..'], {relativeTo: this.route});
}
getType(itemElement: any): typeof itemElement {
return typeof itemElement;
getType(field: typeof EditComponent.prototype.fields[number] | null, value: any): typeof value | string {
return field?.type ?? typeof value;
}
}

View file

@ -13,6 +13,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatDatepickerModule } from '@angular/material/datepicker';
@NgModule({
@ -33,7 +34,8 @@ import { MatTooltipModule } from '@angular/material/tooltip';
ReactiveFormsModule,
MatInputModule,
MatCheckboxModule,
MatTooltipModule
MatTooltipModule,
MatDatepickerModule
]
})
export class SharedComponentsModule {

View file

@ -10,8 +10,9 @@
<ng-container *ngFor="let col of columns" [matColumnDef]="col.prop">
<th mat-header-cell *matHeaderCellDef>{{ col.title }}</th>
<td mat-cell *matCellDef="let item">
<span [ngSwitch]="getType(item[col.prop])">
<span [ngSwitch]="getType(col, item[col.prop])">
<span *ngSwitchCase="'boolean'"><mat-checkbox [checked]="item[col.prop]" disabled="disabled"></mat-checkbox></span>
<span *ngSwitchCase="'date'">{{ item[col.prop] | date }}</span>
<span *ngSwitchDefault>{{ item[col.prop] }}</span>
</span>
</td>

View file

@ -12,7 +12,7 @@ export class TableComponent<T> implements OnInit {
@Input() showHeader = false;
@Input() items: T[] = [];
@Input() loading = false;
@Input() columns: { title: string, prop: string }[] = [];
@Input() columns: { title: string, prop: string, type?: string }[] = [];
@Input() paginationData: PaginationData = {page: 1, limit: 10};
@Input() customActions: { icon: string, label: string, action: (model: T) => void }[] = [];
@Input() allowEditing = true;
@ -30,8 +30,8 @@ export class TableComponent<T> implements OnInit {
return this.columns.map(col => col.prop).concat('actions');
}
getType(itemElement: any): typeof itemElement {
return typeof itemElement;
getType(field: typeof TableComponent.prototype.columns[number] | null, value: any): typeof value | string {
return field?.type ?? typeof value;
}
}

View file

@ -19,13 +19,43 @@
{{ editedFulMode?.name || 'Új teljesitési mód' }}
</mat-card-header>
<mat-card-content>
<app-edit [itemType]="editedFulMode" [createMutation]="modeCreateGQL"
<app-edit [itemType]="editedFulMode" creating [createMutation]="modeCreateGQL"
[updateMutation]="modeEditGQL" [customItem]="editedFulMode" [fields]="[
{name: 'name', title: 'Név'},
{name: 'threshold2', title: 'Kettes %'},
{name: 'threshold3', title: 'Hármas %'},
{name: 'threshold4', title: 'Négyes %'},
{name: 'threshold5', title: 'Ötös %'}
{name: 'threshold2', title: 'Kettes %', type: 'integer'},
{name: 'threshold3', title: 'Hármas %', type: 'integer'},
{name: 'threshold4', title: 'Négyes %', type: 'integer'},
{name: 'threshold5', title: 'Ötös %', type: 'integer'}
]" [beforeSubmit]="beforeFulModeSubmit.bind(this)" [itemSubmitted]="submitFulMode.bind(this)"></app-edit>
</mat-card-content>
</mat-card>
<mat-card style="margin-top: 50px" *ngIf="editingFulMode">
<mat-card-header>
<h3>Követelmények</h3>
</mat-card-header>
<mat-card-content>
<app-list [gql]="requirementListGQL" [queryVariables]="{mode: this.editedFulMode.id}" [columns]="[
{prop: 'name', title: 'Név'},
{prop: 'description', title: 'Leirás'},
{prop: 'minPoints', title: 'Minimum elérendő pontszám'},
{prop: 'maxPoints', title: 'Maximálisan elérhető pontszám'},
{prop: 'deadline', title: 'Határidő', type: 'date'}
]" allowEditing="true" allowNew="true" [editFunction]="editRequirement.bind(this)"
[createFunction]="createRequirement.bind(this)"></app-list>
</mat-card-content>
</mat-card>
<mat-card *ngIf="editingRequirement">
<mat-card-header>
{{ editedRequirement?.name || 'Új követelmény' }}
</mat-card-header>
<mat-card-content>
<app-edit [itemType]="editedRequirement" creating="true" [createMutation]="requirementCreateGQL"
[updateMutation]="requirementEditGQL" [customItem]="editedRequirement" [fields]="[
{name: 'name', title: 'Név'},
{name: 'description', title: 'Leirás'},
{name: 'minPoints', title: 'Minimum elérendő pontszám', type: 'integer'},
{name: 'maxPoints', title: 'Maximálisan elérhető pontszám', type: 'integer'},
{name: 'deadline', title: 'Határidő', type: 'date'}
]" [beforeSubmit]="beforeRequirementSubmit.bind(this)" [itemSubmitted]="submitRequirement.bind(this)"></app-edit>
</mat-card-content>
</mat-card>

View file

@ -6,12 +6,18 @@ import {
CourseGQL,
CreateCourseGQL,
CreateFulfillmentModeGQL,
CreateRequirementGQL,
EditCourseGQL,
EditFulfillmentModeGQL,
EditRequirementGQL,
FulfillmentMode,
FulfillmentModeGQL,
FulfillmentModeListGQL,
FulfillmentModeUpdateInput,
Requirement,
RequirementGQL,
RequirementListGQL,
RequirementUpdateInput,
SubjectGQL
} from '../../../../services/graphql';
@ -26,12 +32,15 @@ export class CourseEditComponent implements OnInit, CustomTitleComponent {
courseId: string;
editedFulMode: FulfillmentMode;
editingFulMode = false;
editedRequirement: Requirement;
editingRequirement = false;
beforeSubmit = () => ({subjectId: +this.route.snapshot.params.subjectId});
constructor(private route: ActivatedRoute, public subjectGQL: SubjectGQL,
public itemGQL: CourseGQL, public editGQL: EditCourseGQL, public createGQL: CreateCourseGQL,
public modeListGQL: FulfillmentModeListGQL, public modeItemGQL: FulfillmentModeGQL, public modeEditGQL: EditFulfillmentModeGQL,
public modeCreateGQL: CreateFulfillmentModeGQL) {
public modeCreateGQL: CreateFulfillmentModeGQL, public requirementListGQL: RequirementListGQL, public requirementGQL: RequirementGQL,
public requirementEditGQL: EditRequirementGQL, public requirementCreateGQL: CreateRequirementGQL) {
this.subjectId = route.snapshot.params.subjectId;
this.courseId = route.snapshot.params.id;
}
@ -59,7 +68,7 @@ export class CourseEditComponent implements OnInit, CustomTitleComponent {
if (val < 0) {
val = 0;
}
((item[prop]) as number) = val > 1 ? val as number / 100 : +val;
((item[prop]) as number) = val > 100 ? 100 : +val;
}
thresh('threshold2');
@ -73,4 +82,23 @@ export class CourseEditComponent implements OnInit, CustomTitleComponent {
this.editedFulMode = null;
this.editingFulMode = false;
}
async editRequirement(item: Requirement): Promise<void> {
this.editingRequirement = true;
this.editedRequirement = item;
}
async createRequirement(item: Requirement): Promise<void> {
this.editingRequirement = true;
this.editedRequirement = null;
}
beforeRequirementSubmit(item: Requirement): Partial<RequirementUpdateInput> {
return {...item, fulfillmentModeId: this.editedFulMode.id};
}
submitRequirement(): void {
this.editingRequirement = false;
this.editedRequirement = null;
}
}

View file

@ -8,6 +8,7 @@ import { Subject, SubjectListGQL } from '../../services/graphql';
styleUrls: ['./subject-list.component.css']
})
export class SubjectListComponent implements OnInit {
itemType: Subject;
constructor(private route: ActivatedRoute, private router: Router, public listGQL: SubjectListGQL) {
}