From d076e0a013ad1e60a81af6107f941e4168ea1fac Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 7 May 2022 20:19:08 +0200 Subject: [PATCH] Fix and improve custom titles, add course support, fix things Implemented hasManyThrough repository with count() method --- .../src/graphql-resolvers/course.resolver.ts | 15 +- .../src/graphql-resolvers/subject.resolver.ts | 2 +- .../src/graphql-resolvers/user.resolver.ts | 2 +- backend/src/graphql-types/list.ts | 4 +- .../repositories/CustomHasManyRepository.ts | 29 ---- .../custom-has-many-repository.ts | 149 ++++++++++++++++++ .../src/repositories/subject.repository.ts | 16 +- backend/src/repositories/user.repository.ts | 14 +- frontend/src/app/app.component.ts | 68 +++++--- frontend/src/app/graphql/course.graphql | 33 ++++ frontend/src/app/graphql/user.graphql | 11 +- .../shared-components/edit/edit.component.ts | 5 + .../shared-components/list/list.component.ts | 10 +- .../course-edit/course-edit.component.html | 4 +- .../course-edit/course-edit.component.ts | 13 +- .../course-list/course-list.component.html | 2 +- .../course-list/course-list.component.ts | 8 +- frontend/src/app/subjects/subjects.module.ts | 4 +- frontend/src/app/users/users.module.ts | 2 +- frontend/src/app/utility/types.ts | 2 + 20 files changed, 293 insertions(+), 100 deletions(-) delete mode 100644 backend/src/repositories/CustomHasManyRepository.ts create mode 100644 backend/src/repositories/custom-has-many-repository.ts create mode 100644 frontend/src/app/graphql/course.graphql diff --git a/backend/src/graphql-resolvers/course.resolver.ts b/backend/src/graphql-resolvers/course.resolver.ts index a2b38b2..add79bc 100644 --- a/backend/src/graphql-resolvers/course.resolver.ts +++ b/backend/src/graphql-resolvers/course.resolver.ts @@ -1,5 +1,5 @@ import { repository } from '@loopback/repository'; -import { CourseRepository } from '../repositories'; +import { CourseRepository, SubjectRepository, UserRepository } 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'; @@ -9,13 +9,20 @@ import { CourseList } from '../graphql-types/course'; @resolver(of => Course) export class CourseResolver { constructor( - @repository('CourseRepository') private courseRepo: CourseRepository + @repository('CourseRepository') private courseRepo: CourseRepository, + @repository('SubjectRepository') private subjectRepo: SubjectRepository, + @repository('UserRepository') private userRepo: UserRepository ) { } @query(returns => CourseList) - async courses(@arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise> { - return listResponse(this.courseRepo, offset, limit, CourseList); + async coursesBySubject(@arg('subject', returns => ID) subject: number, @arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise> { + return listResponse(this.subjectRepo.courses(subject), offset, limit, CourseList); + } + + @query(returns => CourseList) + async coursesByUser(@arg('user', returns => ID) user: number, @arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise> { + return listResponse(this.userRepo.courses(user), offset, limit, CourseList); } @query(returns => Course) diff --git a/backend/src/graphql-resolvers/subject.resolver.ts b/backend/src/graphql-resolvers/subject.resolver.ts index 4ef2eac..a4498fd 100644 --- a/backend/src/graphql-resolvers/subject.resolver.ts +++ b/backend/src/graphql-resolvers/subject.resolver.ts @@ -13,7 +13,7 @@ export class SubjectResolver { @query(returns => SubjectList) async subjects(@arg('offset', returns => Int) offset: number, @arg('limit', returns => Int) limit: number): Promise> { - return listResponse(this.subjectRepo, offset, limit, SubjectList); + return await listResponse(this.subjectRepo, offset, limit, SubjectList); } @query(returns => Subject) diff --git a/backend/src/graphql-resolvers/user.resolver.ts b/backend/src/graphql-resolvers/user.resolver.ts index 7275fb8..0a56e65 100644 --- a/backend/src/graphql-resolvers/user.resolver.ts +++ b/backend/src/graphql-resolvers/user.resolver.ts @@ -75,7 +75,7 @@ export class UserResolver { } @authorized() - @query(returns => [User]) + @query(returns => UserList) async users(@arg('limit', returns => Int) limit: number, @arg('offset', returns => Int) offset: number) { return await listResponse(this.userRepository, offset, limit, UserList); } diff --git a/backend/src/graphql-types/list.ts b/backend/src/graphql-types/list.ts index a0ffa8c..4089f37 100644 --- a/backend/src/graphql-types/list.ts +++ b/backend/src/graphql-types/list.ts @@ -14,7 +14,9 @@ export interface ListResponse { list: T[]; } -export async function listResponse>>(repo: DefaultCrudRepository, offset: number, limit: number, listType: ClassType) { +export type ListRepository = Pick, 'find' | 'count'>; + +export async function listResponse>>(repo: ListRepository, offset: number, limit: number, listType: ClassType) { const list = new listType(); list.list = await repo.find({offset, limit}); list.count = (await repo.count()).count; diff --git a/backend/src/repositories/CustomHasManyRepository.ts b/backend/src/repositories/CustomHasManyRepository.ts deleted file mode 100644 index d35cff9..0000000 --- a/backend/src/repositories/CustomHasManyRepository.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { constrainWhere, Count, DefaultHasManyRepository, Entity, EntityCrudRepository, Where } from '@loopback/repository'; -import { Options } from '@loopback/repository/src/common-types'; -import { HasManyRepository } from '@loopback/repository/src/relations/has-many/has-many.repository'; -import { InclusionResolver } from '@loopback/repository/src/relations/relation.types'; - -export interface CustomHasManyRepositoryFactory { - /** - * Invoke the function to obtain HasManyRepository. - */ - (fkValue: ForeignKeyType): CustomHasManyRepository; - - /** - * Use `resolver` property to obtain an InclusionResolver for this relation. - */ - inclusionResolver: InclusionResolver; -} - -export interface CustomHasManyRepository extends HasManyRepository { - count(where?: Where, options?: Options): Promise; -} - -export class DefaultCustomHasManyRepository> extends DefaultHasManyRepository implements CustomHasManyRepository { - async count(where?: Where, options?: Options) { - const targetRepository = await this.getTargetRepository(); - return targetRepository.count(constrainWhere(where, this.constraint), options); - } -} diff --git a/backend/src/repositories/custom-has-many-repository.ts b/backend/src/repositories/custom-has-many-repository.ts new file mode 100644 index 0000000..2846b40 --- /dev/null +++ b/backend/src/repositories/custom-has-many-repository.ts @@ -0,0 +1,149 @@ +import { + constrainFilter, + constrainWhere, + Count, + DataObject, + DefaultHasManyRepository, + DefaultHasManyThroughRepository, + DefaultTransactionalRepository, + Entity, + EntityCrudRepository, + HasManyRepository, + HasManyThroughRepository, + InclusionResolver, + Options, + Where +} from '@loopback/repository'; +import { Getter } from '@loopback/core'; + +export interface CustomHasManyRepositoryFactory { + /** + * Invoke the function to obtain HasManyRepository. + */ + (fkValue: ForeignKeyType): CustomHasManyRepository; + + /** + * Use `resolver` property to obtain an InclusionResolver for this relation. + */ + inclusionResolver: InclusionResolver; +} + +export interface CustomHasManyRepository extends HasManyRepository { + count(where?: Where, options?: Options): Promise; +} + +export class DefaultCustomHasManyRepository> extends DefaultHasManyRepository implements CustomHasManyRepository { + async count(where?: Where, options?: Options) { + const targetRepository = await this.getTargetRepository(); + return targetRepository.count(constrainWhere(where, this.constraint), options); + } +} + +export interface CustomHasManyThroughRepositoryFactory { + (fkValue: SourceID): CustomHasManyThroughRepository; + + /** + * Use `resolver` property to obtain an InclusionResolver for this relation. + */ + inclusionResolver: InclusionResolver; +} + +export interface CustomHasManyThroughRepository extends HasManyThroughRepository { + count(where?: Where, options?: Options): Promise; +} + +export class DefaultCustomHasManyThroughRepository, + ThroughEntity extends Entity, + ThroughID, + ThroughRepository extends EntityCrudRepository, + > extends DefaultHasManyThroughRepository + implements CustomHasManyThroughRepository { + async count(where?: Where, options?: Options): Promise { + const targetRepository = await this.getTargetRepository(); + const throughRepository = await this.getThroughRepository(); + const sourceConstraint = this.getThroughConstraintFromSource(); + const throughInstances = await throughRepository.find( + constrainFilter(undefined, sourceConstraint), + options?.throughOptions, + ); + const targetConstraint = + this.getTargetConstraintFromThroughModels(throughInstances); + return targetRepository.count( + constrainWhere(where, targetConstraint), + options, + ); + } + +} + +export class CustomCrudRepository extends DefaultTransactionalRepository { + createCustomHasManyRepositoryFactoryFor( + relationName: string, + targetRepositoryGetter: Getter>, + fkName: ForeignKeyName + ): CustomHasManyRepositoryFactory { + const origEntities = this.createHasManyRepositoryFactoryFor(relationName, targetRepositoryGetter); + this.registerInclusionResolver(relationName, origEntities.inclusionResolver); + const entities = function(fkValue: ForeignKeyType | undefined) { + return new DefaultCustomHasManyRepository(targetRepositoryGetter, {[fkName]: fkValue} as unknown as DataObject); + }; + entities.inclusionResolver = origEntities.inclusionResolver; + return entities; + } + + createCustomHasManyThroughFactoryFor( + relationName: string, + targetRepoGetter: Getter>, + throughRepoGetter: Getter>, + sourceKeyName: SourceKeyName, + targetKeyName: TargetKeyName + ): CustomHasManyThroughRepositoryFactory { + const origEntities = this.createHasManyThroughRepositoryFactoryFor(relationName, targetRepoGetter, throughRepoGetter); + this.registerInclusionResolver(relationName, origEntities.inclusionResolver); + const entities = function(fkValue: Through[SourceKeyName] | undefined) { + function getTargetConstraintFromThroughModels( + throughInstances: Through[], + ): DataObject { + const keys = throughInstances.map(instance => instance.getId()); + return {id: keys.length > 1 ? {inq: keys} : keys[0]} as unknown as DataObject; + } + + function getTargetKeys(throughInstances: Through[]): Through[TargetKeyName][] { + return throughInstances.map(instance => instance[targetKeyName]); + } + + function getThroughConstraintFromSource(): DataObject { + return {[sourceKeyName]: fkValue} as unknown as DataObject; + } + + function getTargetIds(targetInstances: Target[]): Through[TargetKeyName][] { + return targetInstances.map(target => target.getId()); + } + + function getThroughConstraintFromTarget(fkValues: Through[TargetKeyName][]): DataObject { + return {[targetKeyName]: fkValues.length > 1 ? {inq: fkValues} : fkValues[0]} as unknown as DataObject; + } + + return new DefaultCustomHasManyThroughRepository, + Through, + ThroughID, + EntityCrudRepository>( + targetRepoGetter, + throughRepoGetter, + getTargetConstraintFromThroughModels, + getTargetKeys, + getThroughConstraintFromSource, + getTargetIds, + getThroughConstraintFromTarget, + ); + }; + entities.inclusionResolver = origEntities.inclusionResolver; + return entities; + } +} diff --git a/backend/src/repositories/subject.repository.ts b/backend/src/repositories/subject.repository.ts index 61fa41d..87aca43 100644 --- a/backend/src/repositories/subject.repository.ts +++ b/backend/src/repositories/subject.repository.ts @@ -1,14 +1,12 @@ import { Getter, inject } from '@loopback/core'; -import { DataObject, DefaultCrudRepository, repository } from '@loopback/repository'; +import { repository } from '@loopback/repository'; import { DatabaseDataSource } from '../datasources'; import { Course, Subject, SubjectRelations } from '../models'; import { UserRepository } from './user.repository'; import { CourseRepository } from './course.repository'; -import { CustomHasManyRepositoryFactory, DefaultCustomHasManyRepository } from './CustomHasManyRepository'; +import { CustomCrudRepository, CustomHasManyRepositoryFactory } from './custom-has-many-repository'; -export class SubjectRepository extends DefaultCrudRepository { +export class SubjectRepository extends CustomCrudRepository { public readonly courses: CustomHasManyRepositoryFactory; @@ -16,12 +14,6 @@ export class SubjectRepository extends DefaultCrudRepository, @repository.getter('CourseRepository') protected courseRepositoryGetter: Getter, ) { super(Subject, dataSource); - const origCourses = this.createHasManyRepositoryFactoryFor('courses', courseRepositoryGetter,); - this.registerInclusionResolver('courses', origCourses.inclusionResolver); - const courses = function(fkValue: number | undefined) { - return new DefaultCustomHasManyRepository(courseRepositoryGetter, {subject_id: fkValue} as DataObject); - }; - courses.inclusionResolver = origCourses.inclusionResolver; - this.courses = courses; + this.courses = this.createCustomHasManyRepositoryFactoryFor('courses', courseRepositoryGetter, 'subjectId'); } } diff --git a/backend/src/repositories/user.repository.ts b/backend/src/repositories/user.repository.ts index 2133b88..55527e7 100644 --- a/backend/src/repositories/user.repository.ts +++ b/backend/src/repositories/user.repository.ts @@ -1,16 +1,17 @@ -import { inject, Getter } from '@loopback/core'; -import { repository, HasManyThroughRepositoryFactory, DefaultTransactionalRepository } from '@loopback/repository'; +import { Getter, inject } from '@loopback/core'; +import { repository } from '@loopback/repository'; import { DatabaseDataSource } from '../datasources'; -import { User, UserRelations, Course, CourseUser } from '../models'; +import { Course, CourseUser, User, UserRelations } from '../models'; import { SubjectRepository } from './subject.repository'; import { CourseUserRepository } from './course-user.repository'; import { CourseRepository } from './course.repository'; +import { CustomCrudRepository, CustomHasManyThroughRepositoryFactory } from './custom-has-many-repository'; -export class UserRepository extends DefaultTransactionalRepository { - public readonly courses: HasManyThroughRepositoryFactory; @@ -18,7 +19,6 @@ export class UserRepository extends DefaultTransactionalRepository, @repository.getter('CourseUserRepository') protected courseUserRepositoryGetter: Getter, @repository.getter('CourseRepository') protected courseRepositoryGetter: Getter, ) { super(User, dataSource); - this.courses = this.createHasManyThroughRepositoryFactoryFor('courses', courseRepositoryGetter, courseUserRepositoryGetter,); - this.registerInclusionResolver('courses', this.courses.inclusionResolver); + this.courses = this.createCustomHasManyThroughFactoryFor('courses', courseRepositoryGetter, courseUserRepositoryGetter, 'userId', 'courseId'); } } diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 3ca12ba..2006876 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -3,7 +3,7 @@ import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Observable } from 'rxjs'; import { filter, map, shareReplay } from 'rxjs/operators'; import { LoginService } from './auth/login.service'; -import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Route, Router, Routes } from '@angular/router'; +import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Params, Route, Router, Routes } from '@angular/router'; import { RouteData } from './app-routing.module'; import { Title } from '@angular/platform-browser'; @@ -27,7 +27,7 @@ export class AppComponent implements OnInit { pageTitle: string; - private activeRouteTitle: string; + private activeComponentVars: object; constructor(private breakpointObserver: BreakpointObserver, public loginService: LoginService, private router: Router, private activeRoute: ActivatedRoute, private title: Title) { @@ -43,7 +43,9 @@ export class AppComponent implements OnInit { item.title = 'NOTFOUND'; } } - this.setPageTitle(); + this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { + this.setPageTitle(); + }); } getRoutes(data: { path: string, routes: Routes }[]): { routes: Routes, path: string, currentRoute: Route }[] { @@ -82,32 +84,44 @@ export class AppComponent implements OnInit { return routeParts; } const data = snapshot.data as RouteData; - if (data.title && snapshot.url.length) { + if (data.title) { routeParts.push({ title: data.title, - url: snapshot.url[0].path + url: snapshot.url.length ? snapshot.url[0].path : undefined, + params: snapshot.params }); } } return routeParts; } + getTitleParts(): string[] { + const routeParts = this.getRouteSegments(this.activeRoute.snapshot); + if (!routeParts.length) { + return []; + } + + function replaceVars(replacements: object, title: string): string { + return Object.keys(replacements).reduce((pv, cv) => pv.replace(':' + cv, replacements[cv]), title); + } + + let titleParts = routeParts.reverse().map(part => replaceVars(part.params, part.title)); + if (this.activeComponentVars) { + titleParts = titleParts.map(part => replaceVars(this.activeComponentVars, part)); + } + return titleParts.map(part => part.startsWith(':') ? '~' : part); + } + setPageTitle(): void { - this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => { - const routeParts = this.getRouteSegments(this.activeRoute.snapshot); - if (!routeParts.length) { - this.pageTitle = 'Szakdolgozat'; - return this.title.setTitle('Szakdolgozat'); - } - const titleParts = routeParts.reverse().map(part => part.title); - if (this.activeRouteTitle) { // TODO: Get title parts from parent components even if parent wasn't opened - titleParts[titleParts.length - 1] = this.activeRouteTitle; - } - let pageTitle = titleParts.reduce((partA, partI) => `${partA} > ${partI}`); - this.pageTitle = pageTitle; - pageTitle += ` | Szakdolgozat`; - this.title.setTitle(pageTitle); - }); + const titleParts = this.getTitleParts(); + if (!titleParts.length) { + this.pageTitle = 'Szakdolgozat'; + return this.title.setTitle('Szakdolgozat'); + } + let pageTitle = titleParts.reduce((partA, partI) => `${partA} > ${partI}`); + this.pageTitle = pageTitle; + pageTitle += ` | Szakdolgozat`; + this.title.setTitle(pageTitle); } async logout(): Promise { @@ -119,23 +133,25 @@ export class AppComponent implements OnInit { return this.menu.filter(item => item.requiredRole === 'admin' ? this.loginService.user?.isAdmin : true); // TODO: Roles } - routeActivated($event: any): void { + async routeActivated($event: any): Promise { if (this.isCustomTitleComponent($event)) { - this.activeRouteTitle = $event.getPageTitle(); + const title = $event.getPageTitleVars(); + this.activeComponentVars = title instanceof Promise ? await title : title; } else { - this.activeRouteTitle = null; + this.activeComponentVars = null; } + this.setPageTitle(); } isCustomTitleComponent(obj: any): obj is CustomTitleComponent { - return obj?.getPageTitle instanceof Function; + return obj?.getPageTitleVars instanceof Function; } } type MenuItem = { path: string, requiredRole: 'admin', title?: string }; // TODO: Role -type RouteSegment = { title: string, url: string }; +type RouteSegment = { title: string, url: string, params: Params }; export interface CustomTitleComponent { - getPageTitle(): string; + getPageTitleVars(): object | Promise; } diff --git a/frontend/src/app/graphql/course.graphql b/frontend/src/app/graphql/course.graphql new file mode 100644 index 0000000..20f30ba --- /dev/null +++ b/frontend/src/app/graphql/course.graphql @@ -0,0 +1,33 @@ +query CourseListBySubject($subject: ID!, $limit: Int!, $offset: Int!) { + coursesBySubject(subject: $subject, limit: $limit, offset: $offset) { + count + list { + id + alias + semester + } + } +} + +query CourseListByUser($user: ID!, $limit: Int!, $offset: Int!) { + coursesByUser(user: $user, limit: $limit, offset: $offset) { + list { + id + alias + semester + } + count + } +} + +query Course($id: ID!) { + course(id: $id) { + id + semester + alias + } +} + +mutation EditCourse($input: CourseUpdateInput!) { + courseUpdate(course: $input) +} diff --git a/frontend/src/app/graphql/user.graphql b/frontend/src/app/graphql/user.graphql index 608fae3..2f73651 100644 --- a/frontend/src/app/graphql/user.graphql +++ b/frontend/src/app/graphql/user.graphql @@ -16,10 +16,13 @@ mutation Logout { query UserList($limit: Int!, $offset: Int!) { users(limit: $limit, offset: $offset) { - id - name - email - isAdmin + list { + id + name + email + isAdmin + } + count } } diff --git a/frontend/src/app/shared-components/edit/edit.component.ts b/frontend/src/app/shared-components/edit/edit.component.ts index 2bb911f..0f8733f 100644 --- a/frontend/src/app/shared-components/edit/edit.component.ts +++ b/frontend/src/app/shared-components/edit/edit.component.ts @@ -55,6 +55,11 @@ export class EditComponent, UT extend } else { this.item = {} as T; this.creating = true; + if (!this.createMutation) { + alert('Nem hozható létre új elem ezzel a tipussal'); + await this.router.navigate(['..'], {relativeTo: this.route}); + return; + } } this.isLoading = false; } diff --git a/frontend/src/app/shared-components/list/list.component.ts b/frontend/src/app/shared-components/list/list.component.ts index f5830fa..1a2f5d2 100644 --- a/frontend/src/app/shared-components/list/list.component.ts +++ b/frontend/src/app/shared-components/list/list.component.ts @@ -3,15 +3,18 @@ import { PageEvent } from '@angular/material/paginator'; import { PaginationData } from '../../utility/pagination-data'; import { Router } from '@angular/router'; import { Query } from 'apollo-angular'; +import { ListVariableType } from '../../utility/types'; @Component({ selector: 'app-list', templateUrl: './list.component.html', styleUrls: ['./list.component.css'] }) -export class ListComponent implements OnInit { +export class ListComponent implements OnInit { - @Input() gql: Query; + @Input() gql: Query>; + @Input() queryVariables: V; @Input() itemType: T; // TODO: Remove @Input() columns: { title: string, prop: keyof T }[]; @Input() allowNew = false; @@ -36,7 +39,8 @@ export class ListComponent { try { this.loading = true; - const {data} = await this.gql.fetch({limit, offset: (page - 1) * limit}).toPromise(); // TODO: Watch + const vars = (this.queryVariables === undefined ? {} : this.queryVariables) as V extends object ? V : {}; + const {data} = await this.gql.fetch({...vars, 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; diff --git a/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.html b/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.html index 3290f4d..19e95c7 100644 --- a/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.html +++ b/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.html @@ -1,4 +1,4 @@ - +]"> diff --git a/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.ts b/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.ts index 50f3e9b..2bf7f79 100644 --- a/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.ts +++ b/frontend/src/app/subjects/subject-edit/courses/course-edit/course-edit.component.ts @@ -1,18 +1,27 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { CustomTitleComponent } from '../../../../app.component'; +import { Course, CourseGQL, EditCourseGQL, SubjectGQL } from '../../../../services/graphql'; @Component({ selector: 'app-course-edit', templateUrl: './course-edit.component.html', styleUrls: ['./course-edit.component.css'] }) -export class CourseEditComponent implements OnInit { +export class CourseEditComponent implements OnInit, CustomTitleComponent { + subjectId: string; + itemType: Course; beforeSubmit = () => ({subjectId: +this.route.snapshot.params.subjectId}); - constructor(private route: ActivatedRoute) { + constructor(private route: ActivatedRoute, public subjectGQL: SubjectGQL, public itemGQL: CourseGQL, public editGQL: EditCourseGQL) { + this.subjectId = route.snapshot.params.subjectId; } ngOnInit(): void { } + getPageTitleVars(): object | Promise { + return this.subjectGQL.fetch({id: this.subjectId}).toPromise().then(subject => ({subjectName: subject.data.subject.name})); + } + } diff --git a/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.html b/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.html index 5dbdcfb..5e2c3ee 100644 --- a/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.html +++ b/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.html @@ -1,4 +1,4 @@ - diff --git a/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.ts b/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.ts index a4a7296..a6e8352 100644 --- a/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.ts +++ b/frontend/src/app/subjects/subject-edit/courses/course-list/course-list.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { CustomTitleComponent } from '../../../../app.component'; -import { Course, SubjectGQL } from '../../../../services/graphql'; +import { Course, CourseListBySubjectGQL, SubjectGQL } from '../../../../services/graphql'; @Component({ selector: 'app-courses', @@ -12,14 +12,14 @@ export class CourseListComponent implements OnInit, CustomTitleComponent { subjectId: string; itemType: Course; - constructor(route: ActivatedRoute, public listGQL: SubjectGQL) { + constructor(route: ActivatedRoute, public subjectGQL: SubjectGQL, public listGQL: CourseListBySubjectGQL) { this.subjectId = route.snapshot.params.subjectId; } ngOnInit(): void { } - getPageTitle(): string { - return 'Custom title'; //TODO: SubjectGQL + getPageTitleVars(): object | Promise { + return this.subjectGQL.fetch({id: this.subjectId}).toPromise().then(subject => ({subjectName: subject.data.subject.name})); } } diff --git a/frontend/src/app/subjects/subjects.module.ts b/frontend/src/app/subjects/subjects.module.ts index 0694244..4bd1333 100644 --- a/frontend/src/app/subjects/subjects.module.ts +++ b/frontend/src/app/subjects/subjects.module.ts @@ -9,10 +9,10 @@ import { CourseListComponent } from './subject-edit/courses/course-list/course-l import { CourseEditComponent } from './subject-edit/courses/course-edit/course-edit.component'; const routes: Routes = [ - {path: '', component: SubjectListComponent, data: {title: 'Tárgyak'} as RouteData}, + {path: '', component: SubjectListComponent, data: {title: ''} as RouteData}, {path: ':id', component: SubjectEditComponent, data: {title: 'Szerkesztés'}}, { - path: ':subjectId/courses', data: {title: 'Kurzusok'}, children: [ + path: ':subjectId/courses', data: {title: ':subjectName'}, children: [ {path: ':id', component: CourseEditComponent, data: {title: 'Szerkesztés'} as RouteData}, {path: '', component: CourseListComponent, data: {title: 'Kurzusok'}} ] diff --git a/frontend/src/app/users/users.module.ts b/frontend/src/app/users/users.module.ts index e793e09..9f8aa9f 100644 --- a/frontend/src/app/users/users.module.ts +++ b/frontend/src/app/users/users.module.ts @@ -7,7 +7,7 @@ import { SharedComponentsModule } from '../shared-components/shared-components.m import { UserEditComponent } from './user-edit/user-edit.component'; const routes: Routes = [ - {path: '', component: UserListComponent, data: {title: 'Felhasználók'} as RouteData}, + {path: '', component: UserListComponent, data: {title: ''} as RouteData}, {path: ':id', component: UserEditComponent, data: {title: 'Szerkesztés'}} ]; diff --git a/frontend/src/app/utility/types.ts b/frontend/src/app/utility/types.ts index eeae322..e2c9c34 100644 --- a/frontend/src/app/utility/types.ts +++ b/frontend/src/app/utility/types.ts @@ -3,3 +3,5 @@ import { Scalars } from '../services/graphql'; export type HasID = { id: Scalars['ID'] }; export type QueryResult = { [entityName: string]: T }; export type MutationInput, U extends HasID> = { input: T }; +export type ListInput = { limit: number; offset: number }; +export type ListVariableType = (V extends object ? V : {}) & ListInput;