Add queries for subjects and courses, list subjects on frontend
Also added logout mutation
This commit is contained in:
parent
8471825e5d
commit
a0bc7d3585
28 changed files with 218 additions and 66 deletions
|
@ -10,9 +10,11 @@ import {
|
|||
UserServiceBindings
|
||||
} from '@loopback/authentication-jwt';
|
||||
import { SzakdolgozatUserService } from './services';
|
||||
import { GraphQLBindings, GraphQLServer } from '@loopback/graphql';
|
||||
import { UserResolver } from './graphql-resolvers/user-resolver';
|
||||
import { GraphQLBindings, GraphQLServer, GraphQLServerOptions } from '@loopback/graphql';
|
||||
import { UserResolver } from './graphql-resolvers/user.resolver';
|
||||
import { SzakdolgozatAuthChecker } from './szakdolgozat-auth-checker';
|
||||
import { CourseResolver } from './graphql-resolvers/course.resolver';
|
||||
import { SubjectResolver } from './graphql-resolvers/subject.resolver';
|
||||
|
||||
export { ApplicationConfig };
|
||||
|
||||
|
@ -25,10 +27,15 @@ export class SzakdolgozatBackendApplication extends BootMixin(
|
|||
super(options);
|
||||
|
||||
const server = this.server(GraphQLServer);
|
||||
this.configure(server.key).to({host: process.env.HOST ?? '0.0.0.0', port: process.env.PORT ?? 3000});
|
||||
this.configure(server.key).to(<GraphQLServerOptions> {
|
||||
host: process.env.HOST ?? '0.0.0.0',
|
||||
port: process.env.PORT ?? 3000
|
||||
});
|
||||
this.getServer(GraphQLServer).then(s => {
|
||||
this.gqlServer = s;
|
||||
s.resolver(UserResolver);
|
||||
s.resolver(CourseResolver);
|
||||
s.resolver(SubjectResolver);
|
||||
});
|
||||
|
||||
// Authentication
|
||||
|
|
31
backend/src/graphql-resolvers/course.resolver.ts
Normal file
31
backend/src/graphql-resolvers/course.resolver.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { repository } from '@loopback/repository';
|
||||
import { CourseRepository } from '../repositories';
|
||||
import { arg, ID, Int, mutation, query, resolver } from '@loopback/graphql';
|
||||
import { Course } from '../models';
|
||||
import { CourseUpdateInput } from '../graphql-types/input/course-update.input';
|
||||
import { listResponse, ListResponse } from '../graphql-types/list';
|
||||
import { CourseList } from '../graphql-types/course';
|
||||
|
||||
@resolver(of => Course)
|
||||
export class CourseResolver {
|
||||
constructor(
|
||||
@repository('CourseRepository') private courseRepo: CourseRepository
|
||||
) {
|
||||
}
|
||||
|
||||
@query(returns => CourseList)
|
||||
async courses(@arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise<ListResponse<Course>> {
|
||||
return listResponse(this.courseRepo, offset, limit, CourseList);
|
||||
}
|
||||
|
||||
@query(returns => Course)
|
||||
async course(@arg('id', returns => ID) id: number): Promise<Course> {
|
||||
return await this.courseRepo.findById(id);
|
||||
}
|
||||
|
||||
@mutation(returns => Boolean)
|
||||
async courseUpdate(@arg('course') input: CourseUpdateInput): Promise<boolean> {
|
||||
await this.courseRepo.updateById(input.id, input);
|
||||
return true;
|
||||
}
|
||||
}
|
29
backend/src/graphql-resolvers/subject.resolver.ts
Normal file
29
backend/src/graphql-resolvers/subject.resolver.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { arg, ID, Int, mutation, query, resolver } from '@loopback/graphql';
|
||||
import { Subject } from '../models';
|
||||
import { SubjectRepository } from '../repositories';
|
||||
import { repository } from '@loopback/repository';
|
||||
import { listResponse, ListResponse } from '../graphql-types/list';
|
||||
import { SubjectList } from '../graphql-types/subject';
|
||||
import { SubjectUpdateInput } from '../graphql-types/input/subject-update.input';
|
||||
|
||||
@resolver(of => Subject)
|
||||
export class SubjectResolver {
|
||||
constructor(@repository('SubjectRepository') private subjectRepo: SubjectRepository) {
|
||||
}
|
||||
|
||||
@query(returns => SubjectList)
|
||||
async subjects(@arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise<ListResponse<Subject>> {
|
||||
return listResponse(this.subjectRepo, offset, limit, SubjectList);
|
||||
}
|
||||
|
||||
@query(returns => Subject)
|
||||
async subject(@arg('id', returns => ID) id: number): Promise<Subject> {
|
||||
return await this.subjectRepo.findById(id);
|
||||
}
|
||||
|
||||
@mutation(returns => Boolean)
|
||||
async subjectUpdate(@arg('subject') input: SubjectUpdateInput): Promise<boolean> {
|
||||
await this.subjectRepo.updateById(input.id, input);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { arg, authorized, GraphQLBindings, Int, mutation, query, resolver, ResolverData } from '@loopback/graphql';
|
||||
import { arg, authorized, GraphQLBindings, ID, mutation, query, resolver, ResolverData } from '@loopback/graphql';
|
||||
import { User } from '../models';
|
||||
import { repository } from '@loopback/repository';
|
||||
import { RevTokenRepository, UserRepository } from '../repositories';
|
||||
|
@ -63,19 +63,19 @@ export class UserResolver {
|
|||
|
||||
@authorized()
|
||||
@query(returns => [User])
|
||||
async find(user: Partial<User>): Promise<User[]> {
|
||||
async findUser(user: Partial<User>): Promise<User[]> {
|
||||
return this.userRepository.find({}); //TODO
|
||||
}
|
||||
|
||||
@authorized()
|
||||
@query(returns => User)
|
||||
async findById(@arg('id', returns => Int) id: number): Promise<User> {
|
||||
@query(returns => User, {name: 'user'})
|
||||
async findById(@arg('id') id: number): Promise<User> {
|
||||
return this.userRepository.findById(id);
|
||||
}
|
||||
|
||||
@authorized()
|
||||
@mutation(returns => Boolean)
|
||||
async updateById(@arg('id', returns => Int) id: number, @arg('user') user: UserUpdateInput): Promise<boolean> {
|
||||
async userUpdate(@arg('id', returns => ID) id: number, @arg('user') user: UserUpdateInput): Promise<boolean> {
|
||||
if (id === +this.user?.id) { //TODO: this.user
|
||||
const loggedInUser = await this.userService.findUserById(this.user.id);
|
||||
if (user.isAdmin !== undefined && loggedInUser.isAdmin !== user.isAdmin) {
|
||||
|
@ -88,7 +88,7 @@ export class UserResolver {
|
|||
|
||||
@authorized()
|
||||
@mutation(returns => Boolean)
|
||||
async deleteById(id: number): Promise<Boolean> {
|
||||
async userDelete(id: number): Promise<Boolean> {
|
||||
await this.userRepository.deleteById(id);
|
||||
return true;
|
||||
}
|
11
backend/src/graphql-types/course.ts
Normal file
11
backend/src/graphql-types/course.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { ListResponse } from './list';
|
||||
import { Course } from '../models';
|
||||
import { field, Int, objectType } from '@loopback/graphql';
|
||||
|
||||
@objectType()
|
||||
export class CourseList implements ListResponse<Course> {
|
||||
@field(returns => Int)
|
||||
count: number;
|
||||
@field(returns => [Course])
|
||||
list: Course[];
|
||||
}
|
15
backend/src/graphql-types/input/course-update.input.ts
Normal file
15
backend/src/graphql-types/input/course-update.input.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { field, ID, inputType } from '@loopback/graphql';
|
||||
import { Course } from '../../models';
|
||||
import { DataObject } from '@loopback/repository';
|
||||
|
||||
@inputType()
|
||||
export class CourseUpdateInput implements Pick<DataObject<Course>, 'id' | 'semester' | 'alias' | 'subjectId'> {
|
||||
@field(returns => ID)
|
||||
id: number;
|
||||
@field()
|
||||
semester?: string;
|
||||
@field()
|
||||
alias?: string;
|
||||
@field()
|
||||
subjectId?: number;
|
||||
}
|
13
backend/src/graphql-types/input/subject-update.input.ts
Normal file
13
backend/src/graphql-types/input/subject-update.input.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { field, ID, inputType } from '@loopback/graphql';
|
||||
import { DataObject } from '@loopback/repository';
|
||||
import { Subject } from '../../models';
|
||||
|
||||
@inputType()
|
||||
export class SubjectUpdateInput implements Pick<DataObject<Subject>, 'id' | 'name' | 'description'> {
|
||||
@field(returns => ID)
|
||||
id: number;
|
||||
@field()
|
||||
name?: string;
|
||||
@field()
|
||||
description?: string;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { field, inputType, objectType } from '@loopback/graphql';
|
||||
import { ClassType, field, inputType } from '@loopback/graphql';
|
||||
import { DefaultCrudRepository, Entity } from '@loopback/repository';
|
||||
|
||||
@inputType()
|
||||
export class ListInput {
|
||||
|
@ -8,10 +9,14 @@ export class ListInput {
|
|||
limit: number;
|
||||
}
|
||||
|
||||
@objectType()
|
||||
export class ListResponse<T> {
|
||||
@field()
|
||||
export interface ListResponse<T> {
|
||||
count: number;
|
||||
@field()
|
||||
list: T[];
|
||||
}
|
||||
|
||||
export async function listResponse<T extends Entity, U extends ListResponse<T>>(repo: DefaultCrudRepository<T, number>, offset: number, limit: number, listType: ClassType<U>) {
|
||||
const list = new listType();
|
||||
list.list = await repo.find({offset, limit});
|
||||
list.count = (await repo.count()).count;
|
||||
return list;
|
||||
}
|
||||
|
|
11
backend/src/graphql-types/subject.ts
Normal file
11
backend/src/graphql-types/subject.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { ListResponse } from './list';
|
||||
import { Subject } from '../models';
|
||||
import { field, Int, objectType } from '@loopback/graphql';
|
||||
|
||||
@objectType()
|
||||
export class SubjectList implements ListResponse<Subject> {
|
||||
@field(returns => Int)
|
||||
count: number;
|
||||
@field(returns => [Subject])
|
||||
list: Subject[];
|
||||
}
|
|
@ -3,29 +3,35 @@ import { Subject } from './subject.model';
|
|||
import { User } from './user.model';
|
||||
import { CourseUser } from './course-user.model';
|
||||
import { Requirement } from './requirement.model';
|
||||
import { field, objectType } from '@loopback/graphql';
|
||||
|
||||
@model()
|
||||
@objectType()
|
||||
export class Course extends Entity {
|
||||
@property({
|
||||
type: 'number',
|
||||
id: true,
|
||||
generated: true,
|
||||
})
|
||||
@field()
|
||||
id?: number;
|
||||
|
||||
@property({
|
||||
type: 'string',
|
||||
required: true,
|
||||
})
|
||||
@field()
|
||||
semester: string;
|
||||
|
||||
@property({
|
||||
type: 'string',
|
||||
required: true,
|
||||
})
|
||||
@field()
|
||||
alias: string;
|
||||
|
||||
@belongsTo(() => Subject)
|
||||
@field()
|
||||
subjectId: number;
|
||||
|
||||
@hasMany(() => User, {through: {model: () => CourseUser}})
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
import { Entity, model, property, hasMany } from '@loopback/repository';
|
||||
import { Entity, hasMany, model, property } from '@loopback/repository';
|
||||
import { Course } from './course.model';
|
||||
import { field, objectType } from '@loopback/graphql';
|
||||
|
||||
@model()
|
||||
@objectType()
|
||||
export class Subject extends Entity {
|
||||
@property({
|
||||
type: 'number',
|
||||
id: true,
|
||||
generated: true,
|
||||
})
|
||||
@field()
|
||||
id?: number;
|
||||
|
||||
@property({
|
||||
type: 'string',
|
||||
required: true,
|
||||
})
|
||||
@field()
|
||||
name: string;
|
||||
|
||||
@property({
|
||||
type: 'string',
|
||||
})
|
||||
@field()
|
||||
description?: string;
|
||||
|
||||
@hasMany(() => Course)
|
||||
|
|
|
@ -33,8 +33,7 @@ export class AppComponent implements OnInit {
|
|||
private activeRouteTitle: string;
|
||||
|
||||
constructor(private breakpointObserver: BreakpointObserver, public loginService: LoginService, private api: ApiService,
|
||||
private router: Router, private login: LoginService, private activeRoute: ActivatedRoute,
|
||||
private title: Title) {
|
||||
private router: Router, private activeRoute: ActivatedRoute, private title: Title) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -115,12 +114,12 @@ export class AppComponent implements OnInit {
|
|||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.api.logout();
|
||||
await this.loginService.logout();
|
||||
await this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
getMenuItems(): MenuItem[] {
|
||||
return this.menu.filter(item => item.requiredRole === 'admin' ? this.login.user?.isAdmin : true); // TODO: Roles
|
||||
return this.menu.filter(item => item.requiredRole === 'admin' ? this.loginService.user?.isAdmin : true); // TODO: Roles
|
||||
}
|
||||
|
||||
routeActivated($event: any): void {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { LoginGQL, UserResult } from '../services/graphql';
|
||||
import { LoginGQL, LogoutGQL, UserResult } from '../services/graphql';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -19,7 +19,7 @@ export class LoginService {
|
|||
return this.userP;
|
||||
}
|
||||
|
||||
constructor(private http: HttpClient, private loginService: LoginGQL) {
|
||||
constructor(private http: HttpClient, private loginGQL: LoginGQL, private logoutGQL: LogoutGQL) {
|
||||
this.tokenP = window.localStorage.getItem('token');
|
||||
this.userP = JSON.parse(window.localStorage.getItem('user'));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export class LoginService {
|
|||
|
||||
async login(email: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
const resp = await this.loginService.mutate({email, password}).toPromise();
|
||||
const resp = await this.loginGQL.mutate({email, password}).toPromise();
|
||||
this.tokenP = resp.data.login.token;
|
||||
this.userP = resp.data.login.user;
|
||||
window.localStorage.setItem('token', this.tokenP);
|
||||
|
@ -50,4 +50,9 @@ export class LoginService {
|
|||
window.localStorage.removeItem('token');
|
||||
window.localStorage.removeItem('user');
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.logoutGQL.mutate().toPromise();
|
||||
this.deleteToken();
|
||||
}
|
||||
}
|
||||
|
|
18
frontend/src/app/graphql/subject.graphql
Normal file
18
frontend/src/app/graphql/subject.graphql
Normal file
|
@ -0,0 +1,18 @@
|
|||
query SubjectList($limit: Int!, $offset: Int!) {
|
||||
subjects(limit: $limit, offset: $offset) {
|
||||
list {
|
||||
name
|
||||
id
|
||||
description
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
query Subject($id: ID!) {
|
||||
subject(id: $id) {
|
||||
id
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
|
@ -9,3 +9,7 @@ mutation Login($email: String!, $password: String!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation Logout {
|
||||
logout
|
||||
}
|
||||
|
|
|
@ -42,9 +42,4 @@ export class ApiService {
|
|||
requestItemCount(url: string): Promise<number> {
|
||||
return this.request('get', url + '/count', {}).then(count => count.count);
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.request('post', '/users/logout', '');
|
||||
this.loginService.deleteToken();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import { Component, Input, OnInit, Type } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Model } from '../../model/model';
|
||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||
import { Query } from 'apollo-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit',
|
||||
templateUrl: './edit.component.html',
|
||||
styleUrls: ['./edit.component.css']
|
||||
})
|
||||
export class EditComponent<T extends Model> implements OnInit {
|
||||
export class EditComponent<T extends { id: number }, U> implements OnInit {
|
||||
|
||||
item?: T;
|
||||
creating = false;
|
||||
isLoading = true;
|
||||
|
||||
@Input() apiPath: string;
|
||||
@Input() gql: Query<U, {}>;
|
||||
@Input() fields: { title: string, name: keyof T, readonly?: (item: T) => boolean }[];
|
||||
@Input() itemType: Type<T>;
|
||||
@Input() itemType: T;
|
||||
/**
|
||||
* Beküldés előtt extra adat hozzáadása
|
||||
*/
|
||||
|
@ -32,7 +32,7 @@ export class EditComponent<T extends Model> implements OnInit {
|
|||
window.localStorage.removeItem(this.router.url);
|
||||
const url = this.route.snapshot.url;
|
||||
if (!this.item && url[url.length - 1].path !== 'new') {
|
||||
this.item = await this.api.request('get', this.apiPath + '/' + this.route.snapshot.url[this.route.snapshot.url.length - 1], {});
|
||||
//this.item = await this.api.request('get', this.apiPath + '/' + this.route.snapshot.url[this.route.snapshot.url.length - 1], {}); - TODO
|
||||
}
|
||||
this.formGroup = this.fb.group(this.fields.reduce((pv, cv) => {
|
||||
const control = new FormControl();
|
||||
|
@ -44,7 +44,7 @@ export class EditComponent<T extends Model> implements OnInit {
|
|||
if (this.item) {
|
||||
this.formGroup.patchValue(this.item);
|
||||
} else {
|
||||
this.item = new this.itemType();
|
||||
this.item = {} as T;
|
||||
this.creating = true;
|
||||
}
|
||||
this.isLoading = false;
|
||||
|
@ -55,9 +55,9 @@ export class EditComponent<T extends Model> implements OnInit {
|
|||
const value = Object.assign({}, this.formGroup.value, this.beforeSubmit(this.item) ?? {});
|
||||
try {
|
||||
if (this.item && !this.creating) {
|
||||
await this.api.request('patch', this.apiPath + '/' + this.item.id, value);
|
||||
//await this.api.request('patch', this.apiPath + '/' + this.item.id, value); - TODO
|
||||
} else {
|
||||
await this.api.request('post', this.apiPath, value);
|
||||
//await this.api.request('post', this.apiPath, value); - TODO
|
||||
}
|
||||
await this.router.navigate(['..'], {relativeTo: this.route});
|
||||
} catch (e) {
|
||||
|
|
|
@ -3,16 +3,17 @@ import { PageEvent } from '@angular/material/paginator';
|
|||
import { PaginationData } from '../../utility/pagination-data';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { Query } from 'apollo-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list',
|
||||
templateUrl: './list.component.html',
|
||||
styleUrls: ['./list.component.css']
|
||||
})
|
||||
export class ListComponent<T extends { id: number }> implements OnInit {
|
||||
export class ListComponent<T extends { id: number }, U extends { [entityName: string]: { count: number; list: T[] } }> implements OnInit {
|
||||
|
||||
@Input() apiPath: string;
|
||||
@Input() itemType: T;
|
||||
@Input() gql: Query<U, { limit: number; offset: number }>;
|
||||
@Input() itemType: T; // TODO: Remove
|
||||
@Input() columns: { title: string, prop: keyof T }[];
|
||||
@Input() allowNew = false;
|
||||
@Input() customActions: { icon: string, label: string, action: (model: T) => void }[] = [];
|
||||
|
@ -36,19 +37,19 @@ export class ListComponent<T extends { id: number }> implements OnInit {
|
|||
async getItems(limit: number, page: number): Promise<void> {
|
||||
try {
|
||||
this.loading = true;
|
||||
if (!this.paginationData.total) {
|
||||
this.paginationData.total = await this.api.requestItemCount(this.apiPath);
|
||||
}
|
||||
const {data} = await this.gql.fetch({limit, offset: (page - 1) * limit}).toPromise(); // TODO: Watch
|
||||
const key = Object.keys(data).filter(k => k !== '__typename')[0];
|
||||
this.paginationData.total = data[key].count;
|
||||
this.paginationData.page = page;
|
||||
this.paginationData.limit = limit;
|
||||
this.items = await this.api.requestPage<T>(this.apiPath, limit, page);
|
||||
this.items = data[key].list;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async editItem(item: T): Promise<void> {
|
||||
window.localStorage.setItem(this.router.url + '/' + item.id, JSON.stringify(item));
|
||||
window.localStorage.setItem(this.router.url + '/' + item.id, JSON.stringify(item)); // TODO: Apollo cache
|
||||
await this.router.navigate([this.router.url, item.id]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<app-edit #edit [itemType]="itemType" apiPath="/courses" [beforeSubmit]="beforeSubmit" [fields]="[
|
||||
<!-- <app-edit [beforeSubmit]="beforeSubmit" [fields]="[
|
||||
{title: 'Félév', name: 'semester'},
|
||||
{title: 'Név', name: 'alias'}
|
||||
]"></app-edit>
|
||||
]"></app-edit> -->
|
||||
|
|
|
@ -7,7 +7,6 @@ import { ActivatedRoute } from '@angular/router';
|
|||
styleUrls: ['./course-edit.component.css']
|
||||
})
|
||||
export class CourseEditComponent implements OnInit {
|
||||
itemType: Course;
|
||||
beforeSubmit = () => ({subjectId: +this.route.snapshot.params.subjectId});
|
||||
|
||||
constructor(private route: ActivatedRoute) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<app-list #list [apiPath]="'/subjects/'+subjectId+'/courses'" [itemType]="itemType" allowNew="true" [columns]="[
|
||||
<app-list [itemType]="itemType" allowNew="true" [columns]="[
|
||||
{prop: 'semester', title: 'Félév'},
|
||||
{prop: 'alias', title: 'Név'}
|
||||
]"></app-list>
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Course } from '../../../../model/course.model';
|
||||
import { ListComponent } from '../../../../shared-components/list/list.component';
|
||||
import { CustomTitleComponent } from '../../../../app.component';
|
||||
import { Course, SubjectGQL } from '../../../../services/graphql';
|
||||
|
||||
@Component({
|
||||
selector: 'app-courses',
|
||||
|
@ -11,10 +10,9 @@ import { CustomTitleComponent } from '../../../../app.component';
|
|||
})
|
||||
export class CourseListComponent implements OnInit, CustomTitleComponent {
|
||||
subjectId: string;
|
||||
itemType = Course;
|
||||
@ViewChild('list') list: ListComponent<Course>;
|
||||
itemType: Course;
|
||||
|
||||
constructor(route: ActivatedRoute) {
|
||||
constructor(route: ActivatedRoute, public listGQL: SubjectGQL) {
|
||||
this.subjectId = route.snapshot.params.subjectId;
|
||||
}
|
||||
|
||||
|
@ -22,6 +20,6 @@ export class CourseListComponent implements OnInit, CustomTitleComponent {
|
|||
}
|
||||
|
||||
getPageTitle(): string {
|
||||
return 'Custom title'; //TODO
|
||||
return 'Custom title'; //TODO: SubjectGQL
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<app-edit [apiPath]="'/subjects'" [itemType]="itemType" [fields]="[
|
||||
<app-edit [gql]="itemGQL" [itemType]="itemType" [fields]="[
|
||||
{title: 'Név', name: 'name'},
|
||||
{title: 'Leirás', name: 'description'}
|
||||
]"></app-edit>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subject } from '../../model/subject.model';
|
||||
import { Subject, SubjectGQL } from '../../services/graphql';
|
||||
|
||||
@Component({
|
||||
selector: 'app-subject-edit',
|
||||
|
@ -7,9 +7,10 @@ import { Subject } from '../../model/subject.model';
|
|||
styleUrls: ['./subject-edit.component.css']
|
||||
})
|
||||
export class SubjectEditComponent implements OnInit {
|
||||
itemType = Subject;
|
||||
itemType: Subject;
|
||||
|
||||
constructor() { }
|
||||
constructor(public itemGQL: SubjectGQL) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<app-list apiPath="/subjects" [itemType]="itemType" allowNew="true" [columns]="[
|
||||
<app-list [gql]="listGQL" allowNew="true" [columns]="[
|
||||
{title: 'Név', prop: 'name'},
|
||||
{title: 'Leirás', prop: 'description'}
|
||||
]" [customActions]="[
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Subject } from '../../model/subject.model';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Subject, SubjectListGQL } from '../../services/graphql';
|
||||
|
||||
@Component({
|
||||
selector: 'app-subject-list',
|
||||
|
@ -8,9 +8,8 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||
styleUrls: ['./subject-list.component.css']
|
||||
})
|
||||
export class SubjectListComponent implements OnInit {
|
||||
itemType = Subject;
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router) {
|
||||
constructor(private route: ActivatedRoute, private router: Router, public listGQL: SubjectListGQL) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<app-edit [apiPath]="'/users'" [itemType]="itemType" [fields]="[
|
||||
<app-edit [itemType]="itemType" [fields]="[
|
||||
{title: 'E-mail', name: 'email'},
|
||||
{title: 'Név', name: 'name'},
|
||||
{title: 'Admin', name: 'isAdmin', readonly: isEditingSelf}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { User } from '../../model/user.model';
|
||||
import { LoginService } from '../../auth/login.service';
|
||||
import { UserResult } from '../../services/graphql';
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-edit',
|
||||
|
@ -8,7 +8,7 @@ import { LoginService } from '../../auth/login.service';
|
|||
styleUrls: ['./user-edit.component.css']
|
||||
})
|
||||
export class UserEditComponent implements OnInit {
|
||||
itemType = User;
|
||||
itemType: UserResult;
|
||||
isEditingSelf = user => user.id === this.userService.user.id;
|
||||
|
||||
constructor(private userService: LoginService) { }
|
||||
|
|
Loading…
Reference in a new issue