mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-03-07 15:22:23 +00:00
feat: assessments module
This commit is contained in:
@@ -63,6 +63,7 @@ import {AuthModule} from './modules/auth/auth.module';
|
|||||||
import {BackgroundModule} from './modules/background/background.module';
|
import {BackgroundModule} from './modules/background/background.module';
|
||||||
import {LibraryModule} from './modules/library/library.module';
|
import {LibraryModule} from './modules/library/library.module';
|
||||||
import {StorageProvider} from './modules/storage/storage.provider';
|
import {StorageProvider} from './modules/storage/storage.provider';
|
||||||
|
import {AssessmentsModule} from './modules/assessments/assessments.module';
|
||||||
|
|
||||||
registerLocaleData(localeDe);
|
registerLocaleData(localeDe);
|
||||||
|
|
||||||
@@ -129,6 +130,7 @@ export function createTranslateLoader(http: HttpClient) {
|
|||||||
AboutModule,
|
AboutModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
|
AssessmentsModule,
|
||||||
BackgroundModule,
|
BackgroundModule,
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
|||||||
2005
src/app/modules/assessments/assessment-mock-data.json
Normal file
2005
src/app/modules/assessments/assessment-mock-data.json
Normal file
File diff suppressed because it is too large
Load Diff
78
src/app/modules/assessments/assessments.module.ts
Normal file
78
src/app/modules/assessments/assessments.module.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {AssessmentListItemComponent} from './types/assessment/assessment-list-item.component';
|
||||||
|
import {AssessmentBaseInfoComponent} from './types/assessment/assessment-base-info.component';
|
||||||
|
import {AssessmentDetailComponent} from './types/assessment/assessment-detail.component';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {FormsModule} from '@angular/forms';
|
||||||
|
import {IonicModule} from '@ionic/angular';
|
||||||
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {DataModule} from '../data/data.module';
|
||||||
|
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||||
|
import {CourseOfStudyAssessmentComponent} from './types/course-of-study/course-of-study-assessment.component';
|
||||||
|
import {AssessmentsPageComponent} from './page/assessments-page.component';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {AuthGuardService} from '../auth/auth-guard.service';
|
||||||
|
import {MomentModule} from 'ngx-moment';
|
||||||
|
import {AssessmentsListItemComponent} from './list/assessments-list-item.component';
|
||||||
|
import {AssessmentsDataListComponent} from './list/assessments-data-list.component';
|
||||||
|
import {AssessmentsDetailComponent} from './detail/assessments-detail.component';
|
||||||
|
import {AssessmentsProvider} from './assessments.provider';
|
||||||
|
import {AssessmentsSimpleDataListComponent} from './list/assessments-simple-data-list.component';
|
||||||
|
import {ProtectedRoutes} from '../auth/protected.routes';
|
||||||
|
|
||||||
|
const routes: ProtectedRoutes = [
|
||||||
|
{
|
||||||
|
path: 'assessments',
|
||||||
|
component: AssessmentsPageComponent,
|
||||||
|
data: {authProvider: 'default'},
|
||||||
|
canActivate: [AuthGuardService],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'assessments/detail/:uid',
|
||||||
|
component: AssessmentsDetailComponent,
|
||||||
|
data: {authProvider: 'default'},
|
||||||
|
canActivate: [AuthGuardService],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AssessmentListItemComponent,
|
||||||
|
AssessmentBaseInfoComponent,
|
||||||
|
AssessmentDetailComponent,
|
||||||
|
AssessmentsListItemComponent,
|
||||||
|
CourseOfStudyAssessmentComponent,
|
||||||
|
AssessmentsPageComponent,
|
||||||
|
AssessmentsDataListComponent,
|
||||||
|
AssessmentsDetailComponent,
|
||||||
|
AssessmentsSimpleDataListComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
IonicModule,
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
TranslateModule,
|
||||||
|
DataModule,
|
||||||
|
ThingTranslateModule,
|
||||||
|
MomentModule,
|
||||||
|
],
|
||||||
|
providers: [AssessmentsProvider],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class AssessmentsModule {}
|
||||||
85
src/app/modules/assessments/assessments.provider.ts
Normal file
85
src/app/modules/assessments/assessments.provider.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ConfigProvider} from '../config/config.provider';
|
||||||
|
import {SCAssessment} from '@openstapps/core';
|
||||||
|
import {DefaultAuthService} from '../auth/default-auth.service';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AssessmentsProvider {
|
||||||
|
assessmentPath = 'assessments';
|
||||||
|
|
||||||
|
// usually this wouldn't be necessary, but the assessment service
|
||||||
|
// is very aggressive about too many requests being made to the server
|
||||||
|
cache?: Promise<SCAssessment[]>;
|
||||||
|
|
||||||
|
cacheTimestamp = 0;
|
||||||
|
|
||||||
|
// 15 minutes
|
||||||
|
cacheMaxAge = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly configProvider: ConfigProvider,
|
||||||
|
readonly defaultAuth: DefaultAuthService,
|
||||||
|
readonly http: HttpClient,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getAssessments(
|
||||||
|
accessToken?: string | null,
|
||||||
|
forceFetch = false,
|
||||||
|
): Promise<SCAssessment[]> {
|
||||||
|
// again, this is a hack to get around the fact that the assessment service
|
||||||
|
// is very aggressive how many requests you can make, so it can happen
|
||||||
|
// during development that simply by reloading pages over and over again
|
||||||
|
// the assessment service will block you
|
||||||
|
if (accessToken === 'mock' && !this.cache) {
|
||||||
|
this.cacheTimestamp = Date.now();
|
||||||
|
this.cache = import('./assessment-mock-data.json').then(
|
||||||
|
it => it.data as SCAssessment[],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.cache &&
|
||||||
|
!forceFetch &&
|
||||||
|
Date.now() - this.cacheTimestamp < this.cacheMaxAge
|
||||||
|
) {
|
||||||
|
return await this.cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = this.configProvider.config.app.features.extern?.hisometry.url;
|
||||||
|
if (!url) throw new Error('Config lacks url for hisometry');
|
||||||
|
|
||||||
|
this.cache = this.http
|
||||||
|
.get<{data: SCAssessment[]}>(`${url}/${this.assessmentPath}`, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${
|
||||||
|
accessToken ?? (await this.defaultAuth.getValidToken()).accessToken
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.toPromise()
|
||||||
|
.then(it => {
|
||||||
|
this.cacheTimestamp = Date.now();
|
||||||
|
return it?.data ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, ViewChild} from '@angular/core';
|
||||||
|
import {flatMap} from 'lodash-es';
|
||||||
|
import {ActivatedRoute} from '@angular/router';
|
||||||
|
import {AssessmentsProvider} from '../assessments.provider';
|
||||||
|
import {
|
||||||
|
DataDetailComponent,
|
||||||
|
ExternalDataLoadEvent,
|
||||||
|
} from '../../data/detail/data-detail.component';
|
||||||
|
import {ViewWillEnter} from '@ionic/angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessments-detail',
|
||||||
|
templateUrl: 'assessments-detail.html',
|
||||||
|
styleUrls: ['assessments-detail.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentsDetailComponent implements ViewWillEnter {
|
||||||
|
constructor(
|
||||||
|
readonly route: ActivatedRoute,
|
||||||
|
readonly assessmentsProvider: AssessmentsProvider,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@ViewChild(DataDetailComponent)
|
||||||
|
detailComponent: DataDetailComponent;
|
||||||
|
|
||||||
|
getItem(event: ExternalDataLoadEvent) {
|
||||||
|
this.assessmentsProvider
|
||||||
|
.getAssessments(
|
||||||
|
this.route.snapshot.queryParamMap.get('token'),
|
||||||
|
event.forceReload,
|
||||||
|
)
|
||||||
|
.then(assessments => {
|
||||||
|
const assessment = assessments.find(it => it.uid === event.uid);
|
||||||
|
event.resolve(
|
||||||
|
assessment
|
||||||
|
? assessment
|
||||||
|
: flatMap(assessments, it =>
|
||||||
|
Array.isArray(it.superAssessments)
|
||||||
|
? it.superAssessments.map(superAssessment => ({
|
||||||
|
...superAssessment,
|
||||||
|
origin: it.origin,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
).find(it => it?.uid === event.uid),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ionViewWillEnter() {
|
||||||
|
await this.detailComponent.ionViewWillEnter();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/app/modules/assessments/detail/assessments-detail.html
Normal file
32
src/app/modules/assessments/detail/assessments-detail.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar color="primary">
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
<ion-menu-button></ion-menu-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>{{ 'data.detail.TITLE' | translate }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<stapps-data-detail
|
||||||
|
[externalData]="true"
|
||||||
|
(loadItem)="getItem($event)"
|
||||||
|
[defaultHeader]="false"
|
||||||
|
>
|
||||||
|
<ng-template let-item>
|
||||||
|
<assessment-detail [item]="item"></assessment-detail>
|
||||||
|
</ng-template>
|
||||||
|
</stapps-data-detail>
|
||||||
18
src/app/modules/assessments/detail/assessments-detail.scss
Normal file
18
src/app/modules/assessments/detail/assessments-detail.scss
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
stapps-data-detail {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
|
import {SCThings} from '@openstapps/core';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessments-data-list',
|
||||||
|
templateUrl: './assessments-data-list.html',
|
||||||
|
styleUrls: ['./assessments-data-list.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentsDataListComponent {
|
||||||
|
/**
|
||||||
|
* All SCThings to display
|
||||||
|
*/
|
||||||
|
@Input() items?: SCThings[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output binding to trigger pagination fetch
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @angular-eslint/no-output-rename
|
||||||
|
@Output('loadmore') loadMore = new EventEmitter<void>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits when scroll view should reset to top
|
||||||
|
*/
|
||||||
|
@Input() resetToTop?: Observable<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the list is to display SCThings of a single type
|
||||||
|
*/
|
||||||
|
@Input() singleType = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signalizes that the data is being loaded
|
||||||
|
*/
|
||||||
|
@Input() loading = true;
|
||||||
|
}
|
||||||
32
src/app/modules/assessments/list/assessments-data-list.html
Normal file
32
src/app/modules/assessments/list/assessments-data-list.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stapps-data-list
|
||||||
|
[items]="items"
|
||||||
|
[loading]="loading"
|
||||||
|
[singleType]="singleType"
|
||||||
|
[resetToTop]="resetToTop"
|
||||||
|
(loadmore)="loadMore.emit($event)"
|
||||||
|
>
|
||||||
|
<ng-template let-item>
|
||||||
|
<assessments-list-item
|
||||||
|
[item]="item"
|
||||||
|
[hideThumbnail]="singleType"
|
||||||
|
></assessments-list-item>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container header>
|
||||||
|
<ng-content select="[header]"></ng-content>
|
||||||
|
</ng-container>
|
||||||
|
</stapps-data-list>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCThings} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessments-list-item',
|
||||||
|
templateUrl: 'assessments-list-item.html',
|
||||||
|
styleUrls: ['assessments-list-item.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentsListItemComponent {
|
||||||
|
/**
|
||||||
|
* Whether the list item should show a thumbnail
|
||||||
|
*/
|
||||||
|
@Input() hideThumbnail = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item to show
|
||||||
|
*/
|
||||||
|
@Input() item: SCThings;
|
||||||
|
}
|
||||||
24
src/app/modules/assessments/list/assessments-list-item.html
Normal file
24
src/app/modules/assessments/list/assessments-list-item.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stapps-data-list-item
|
||||||
|
[item]="item"
|
||||||
|
[hideThumbnail]="hideThumbnail"
|
||||||
|
[favoriteButton]="false"
|
||||||
|
>
|
||||||
|
<ng-template let-data>
|
||||||
|
<stapps-assessment-list-item [item]="data"></stapps-assessment-list-item>
|
||||||
|
</ng-template>
|
||||||
|
</stapps-data-list-item>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {SCThings} from '@openstapps/core';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessments-simple-data-list',
|
||||||
|
templateUrl: 'assessments-simple-data-list.html',
|
||||||
|
styleUrls: ['assessments-simple-data-list.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentsSimpleDataListComponent implements OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* All SCThings to display
|
||||||
|
*/
|
||||||
|
_items?: Promise<SCThings[] | undefined>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not the list is to display SCThings of a single type
|
||||||
|
*/
|
||||||
|
@Input() singleType = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List header
|
||||||
|
*/
|
||||||
|
@Input() listHeader?: string;
|
||||||
|
|
||||||
|
@Input() set items(items: SCThings[] | undefined) {
|
||||||
|
this._items = new Promise(resolve => resolve(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly dataRoutingService: DataRoutingService,
|
||||||
|
readonly router: Router,
|
||||||
|
readonly activatedRoute: ActivatedRoute,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.dataRoutingService.itemSelectListener().subscribe(thing => {
|
||||||
|
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||||
|
queryParams: {
|
||||||
|
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
for (const subscription of this.subscriptions) subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stapps-simple-data-list
|
||||||
|
[singleType]="singleType"
|
||||||
|
[items]="_items"
|
||||||
|
[listHeader]="listHeader"
|
||||||
|
[autoRouting]="false"
|
||||||
|
>
|
||||||
|
<ng-template let-item>
|
||||||
|
<assessments-list-item
|
||||||
|
[item]="item"
|
||||||
|
[hideThumbnail]="singleType"
|
||||||
|
></assessments-list-item>
|
||||||
|
</ng-template>
|
||||||
|
</stapps-simple-data-list>
|
||||||
125
src/app/modules/assessments/page/assessments-page.component.ts
Normal file
125
src/app/modules/assessments/page/assessments-page.component.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {AssessmentsProvider} from '../assessments.provider';
|
||||||
|
import {SCAssessment, SCCourseOfStudy} from '@openstapps/core';
|
||||||
|
import {groupBy, mapValues} from 'lodash-es';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
|
import {NGXLogger} from 'ngx-logger';
|
||||||
|
import {materialSharedAxisX} from '../../../animation/material-motion';
|
||||||
|
import {SharedAxisChoreographer} from '../../../animation/animation-choreographer';
|
||||||
|
import {DataProvider, DataScope} from '../../data/data.provider';
|
||||||
|
import {DataRoutingService} from '../../data/data-routing.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-assessments-page',
|
||||||
|
templateUrl: 'assessments-page.html',
|
||||||
|
styleUrls: ['assessments-page.scss'],
|
||||||
|
animations: [materialSharedAxisX],
|
||||||
|
})
|
||||||
|
export class AssessmentsPageComponent
|
||||||
|
implements OnInit, AfterViewInit, OnDestroy
|
||||||
|
{
|
||||||
|
assessments: Promise<
|
||||||
|
Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
assessments: SCAssessment[];
|
||||||
|
courseOfStudy: Promise<SCCourseOfStudy | undefined>;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
assessmentKeys: string[] = [];
|
||||||
|
|
||||||
|
routingSubscription: Subscription;
|
||||||
|
|
||||||
|
@ViewChild('segment') segmentView!: HTMLIonSegmentElement;
|
||||||
|
|
||||||
|
sharedAxisChoreographer: SharedAxisChoreographer<string> =
|
||||||
|
new SharedAxisChoreographer<string>('', []);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly logger: NGXLogger,
|
||||||
|
readonly assessmentsProvider: AssessmentsProvider,
|
||||||
|
readonly dataProvider: DataProvider,
|
||||||
|
readonly activatedRoute: ActivatedRoute,
|
||||||
|
readonly dataRoutingService: DataRoutingService,
|
||||||
|
readonly router: Router,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
this.segmentView.value = this.sharedAxisChoreographer.currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.routingSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.routingSubscription = this.dataRoutingService
|
||||||
|
.itemSelectListener()
|
||||||
|
.subscribe(thing => {
|
||||||
|
void this.router.navigate(['assessments', 'detail', thing.uid], {
|
||||||
|
queryParams: {
|
||||||
|
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activatedRoute.queryParams.subscribe(parameters => {
|
||||||
|
try {
|
||||||
|
this.assessments = this.assessmentsProvider
|
||||||
|
.getAssessments(parameters.token)
|
||||||
|
.then(assessments =>
|
||||||
|
groupBy(assessments, it => it.courseOfStudy?.uid ?? 'unknown'),
|
||||||
|
)
|
||||||
|
.then(it => {
|
||||||
|
this.assessmentKeys = Object.keys(it);
|
||||||
|
this.sharedAxisChoreographer = new SharedAxisChoreographer(
|
||||||
|
this.assessmentKeys[0],
|
||||||
|
this.assessmentKeys,
|
||||||
|
);
|
||||||
|
if (this.segmentView) {
|
||||||
|
this.segmentView.value =
|
||||||
|
this.sharedAxisChoreographer.currentValue;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
})
|
||||||
|
.then(groups =>
|
||||||
|
mapValues(groups, (group, uid) => ({
|
||||||
|
assessments: group,
|
||||||
|
courseOfStudy: this.dataProvider
|
||||||
|
.get(uid, DataScope.Remote)
|
||||||
|
.catch(
|
||||||
|
() => group[0].courseOfStudy,
|
||||||
|
) as Promise<SCCourseOfStudy>,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(error);
|
||||||
|
this.assessments = Promise.resolve({});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/app/modules/assessments/page/assessments-page.html
Normal file
49
src/app/modules/assessments/page/assessments-page.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<ion-header>
|
||||||
|
<ion-toolbar color="primary">
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
<ion-menu-button></ion-menu-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>{{ 'assessments.TITLE' | translate }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-segment
|
||||||
|
#segment
|
||||||
|
(ionChange)="sharedAxisChoreographer.changeViewForState(segment.value)"
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
<ion-segment-button *ngFor="let key of assessmentKeys" [value]="key">
|
||||||
|
<div *ngIf="assessments | async as assessments">
|
||||||
|
<ion-label
|
||||||
|
*ngIf="
|
||||||
|
assessments[key].courseOfStudy | async as course;
|
||||||
|
else defaultLabel
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ 'name' | thingTranslate: course }} ({{
|
||||||
|
'academicDegree' | thingTranslate: course
|
||||||
|
}})
|
||||||
|
</ion-label>
|
||||||
|
</div>
|
||||||
|
<ng-template #defaultLabel>
|
||||||
|
<ion-label>{{ key }}</ion-label>
|
||||||
|
</ng-template>
|
||||||
|
</ion-segment-button>
|
||||||
|
</ion-segment>
|
||||||
|
<ion-content>
|
||||||
|
<div
|
||||||
|
[ngSwitch]="sharedAxisChoreographer.currentValue"
|
||||||
|
[@materialSharedAxisX]="sharedAxisChoreographer.animationState"
|
||||||
|
(@materialSharedAxisX.done)="sharedAxisChoreographer.animationDone()"
|
||||||
|
*ngIf="assessments | async as items"
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<course-of-study-assessment
|
||||||
|
[assessments]="items[sharedAxisChoreographer.currentValue].assessments"
|
||||||
|
[courseOfStudy]="
|
||||||
|
items[sharedAxisChoreographer.currentValue].courseOfStudy | async
|
||||||
|
"
|
||||||
|
></course-of-study-assessment>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
3
src/app/modules/assessments/page/assessments-page.scss
Normal file
3
src/app/modules/assessments/page/assessments-page.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCAssessment} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessment-base-info',
|
||||||
|
templateUrl: 'assessment-base-info.html',
|
||||||
|
styleUrls: ['assessment-base-info.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentBaseInfoComponent {
|
||||||
|
_item: SCAssessment;
|
||||||
|
|
||||||
|
passed = false;
|
||||||
|
|
||||||
|
@Input() set item(item: SCAssessment) {
|
||||||
|
this._item = item;
|
||||||
|
this.passed = !/^(5[,.]0)|FX?$/i.test(item.grade);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ion-label [color]="passed ? undefined : 'danger'"
|
||||||
|
>{{
|
||||||
|
(_item.grade | isNumeric)
|
||||||
|
? (_item.grade
|
||||||
|
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1')
|
||||||
|
: ''
|
||||||
|
}}
|
||||||
|
{{ 'status' | thingTranslate: _item | titlecase }},
|
||||||
|
{{ 'attempt' | propertyNameTranslate: _item }}
|
||||||
|
{{ _item.attempt }}
|
||||||
|
</ion-label>
|
||||||
|
<ion-note>
|
||||||
|
{{ _item.ects }}
|
||||||
|
{{ 'ects' | propertyNameTranslate: _item }}</ion-note
|
||||||
|
>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCAssessment} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'assessment-detail',
|
||||||
|
templateUrl: 'assessment-detail.html',
|
||||||
|
styleUrls: ['assessment-detail.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentDetailComponent {
|
||||||
|
@Input() item: SCAssessment;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<ion-note *ngIf="item.courseOfStudy as courseOfStudy">
|
||||||
|
{{ $any('courseOfStudy' | propertyNameTranslate: item) | titlecase }}:
|
||||||
|
{{ 'name' | thingTranslate: $any(courseOfStudy) }}
|
||||||
|
({{ 'academicDegree' | thingTranslate: $any(courseOfStudy) }})
|
||||||
|
</ion-note>
|
||||||
|
<ion-list class="container">
|
||||||
|
<ion-item lines="none">
|
||||||
|
<assessment-base-info [item]="item"></assessment-base-info>
|
||||||
|
</ion-item>
|
||||||
|
<h2 *ngIf="item.superAssessments">
|
||||||
|
{{ $any('superAssessments' | propertyNameTranslate: item) | titlecase }}
|
||||||
|
</h2>
|
||||||
|
<assessments-simple-data-list
|
||||||
|
*ngIf="item.superAssessments"
|
||||||
|
[items]="$any(item.superAssessments)"
|
||||||
|
[singleType]="true"
|
||||||
|
></assessments-simple-data-list>
|
||||||
|
</ion-list>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
stapps-data-list {
|
||||||
|
height: 100px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCAssessment} from '@openstapps/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'stapps-assessment-list-item',
|
||||||
|
templateUrl: './assessment-list-item.html',
|
||||||
|
styleUrls: ['./assessment-list-item.scss'],
|
||||||
|
})
|
||||||
|
export class AssessmentListItemComponent {
|
||||||
|
@Input() item: SCAssessment;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<h2 class="name">
|
||||||
|
{{ 'name' | thingTranslate: item }}
|
||||||
|
{{ item.date ? (item.date | amDateFormat) : '' }}
|
||||||
|
</h2>
|
||||||
|
<assessment-base-info [item]="item"></assessment-base-info>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SCAssessment, SCCourseOfStudyWithoutReferences} from '@openstapps/core';
|
||||||
|
import {sum, sumBy} from 'lodash-es';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'course-of-study-assessment',
|
||||||
|
templateUrl: 'course-of-study-assessment.html',
|
||||||
|
styleUrls: ['course-of-study-assessment.scss'],
|
||||||
|
})
|
||||||
|
export class CourseOfStudyAssessmentComponent {
|
||||||
|
@Input() courseOfStudy: SCCourseOfStudyWithoutReferences | null;
|
||||||
|
|
||||||
|
_assessments: SCAssessment[];
|
||||||
|
|
||||||
|
grade = 0;
|
||||||
|
|
||||||
|
ects = 0;
|
||||||
|
|
||||||
|
@Input() set assessments(value: SCAssessment[]) {
|
||||||
|
this._assessments = value;
|
||||||
|
|
||||||
|
const grades = this._assessments
|
||||||
|
// TODO: find out if this is correct
|
||||||
|
.filter(assessment => assessment.status === 'bestanden')
|
||||||
|
.map(assessment => Number(assessment.grade))
|
||||||
|
.filter(grade => !Number.isNaN(grade));
|
||||||
|
this.grade = grades.length > 0 ? sum(grades) / grades.length : 0;
|
||||||
|
this.ects = sumBy(this._assessments, 'ects');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 StApps
|
||||||
|
~ This program is free software: you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU General Public License as published by the Free
|
||||||
|
~ Software Foundation, version 3.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with
|
||||||
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<assessments-data-list
|
||||||
|
[singleType]="true"
|
||||||
|
[items]="_assessments"
|
||||||
|
[loading]="false"
|
||||||
|
>
|
||||||
|
<div header>
|
||||||
|
<section>
|
||||||
|
<h3>{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}</h3>
|
||||||
|
<p>
|
||||||
|
{{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}:
|
||||||
|
{{
|
||||||
|
grade
|
||||||
|
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1'
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<p>{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}</p>
|
||||||
|
</section>
|
||||||
|
<h3>
|
||||||
|
{{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</assessments-data-list>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
@@ -18,6 +18,10 @@ export class AuthGuardService implements CanActivate {
|
|||||||
route: ActivatedProtectedRouteSnapshot,
|
route: ActivatedProtectedRouteSnapshot,
|
||||||
_state: RouterStateSnapshot,
|
_state: RouterStateSnapshot,
|
||||||
) {
|
) {
|
||||||
|
if (route.queryParamMap.get('token')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.authHelper
|
await this.authHelper
|
||||||
.getProvider(route.data.authProvider)
|
.getProvider(route.data.authProvider)
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input} from '@angular/core';
|
import {Component, Input, TemplateRef} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
|
import {DataListContext} from '../list/data-list.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
@@ -28,4 +29,6 @@ export class DataDetailContentComponent {
|
|||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
@Input() item: SCThings;
|
@Input() item: SCThings;
|
||||||
|
|
||||||
|
@Input() contentTemplateRef?: TemplateRef<DataListContext<SCThings>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,99 @@
|
|||||||
<stapps-title-card [item]="item"> </stapps-title-card>
|
<stapps-title-card [item]="item"> </stapps-title-card>
|
||||||
<div [ngSwitch]="item.type">
|
<ng-container
|
||||||
<stapps-article-detail-content
|
*ngTemplateOutlet="
|
||||||
[item]="item"
|
contentTemplateRef || defaultContent;
|
||||||
*ngSwitchCase="'article'"
|
context: {$implicit: item}
|
||||||
></stapps-article-detail-content>
|
"
|
||||||
<stapps-catalog-detail-content
|
>
|
||||||
[item]="item"
|
</ng-container>
|
||||||
*ngSwitchCase="'catalog'"
|
|
||||||
></stapps-catalog-detail-content>
|
|
||||||
<stapps-date-series-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'date series'"
|
|
||||||
></stapps-date-series-detail-content>
|
|
||||||
<stapps-dish-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'dish'"
|
|
||||||
></stapps-dish-detail-content>
|
|
||||||
<stapps-event-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'academic event'"
|
|
||||||
></stapps-event-detail-content>
|
|
||||||
<stapps-event-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'sport course'"
|
|
||||||
></stapps-event-detail-content>
|
|
||||||
<stapps-favorite-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'favorite'"
|
|
||||||
></stapps-favorite-detail-content>
|
|
||||||
<stapps-message-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'message'"
|
|
||||||
></stapps-message-detail-content>
|
|
||||||
<stapps-person-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'person'"
|
|
||||||
></stapps-person-detail-content>
|
|
||||||
<stapps-place-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'building'"
|
|
||||||
></stapps-place-detail-content>
|
|
||||||
<stapps-place-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'floor'"
|
|
||||||
></stapps-place-detail-content>
|
|
||||||
<stapps-place-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'point of interest'"
|
|
||||||
></stapps-place-detail-content>
|
|
||||||
<stapps-place-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'room'"
|
|
||||||
></stapps-place-detail-content>
|
|
||||||
<stapps-semester-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'semester'"
|
|
||||||
></stapps-semester-detail-content>
|
|
||||||
<stapps-video-detail-content
|
|
||||||
[item]="item"
|
|
||||||
*ngSwitchCase="'video'"
|
|
||||||
></stapps-video-detail-content>
|
|
||||||
<ng-container *ngSwitchDefault>
|
|
||||||
<ion-item class="ion-text-wrap" lines="inset">
|
|
||||||
<ion-thumbnail slot="start" class="ion-margin-end">
|
|
||||||
<ion-icon color="medium" [attr.name]="item.type | dataIcon"></ion-icon>
|
|
||||||
</ion-thumbnail>
|
|
||||||
<ion-grid>
|
|
||||||
<ion-row>
|
|
||||||
<ion-col>
|
|
||||||
<div class="ion-text-wrap">
|
|
||||||
<h2 class="name">{{ item.name }}</h2>
|
|
||||||
<ion-note>{{ item.type }}</ion-note>
|
|
||||||
</div>
|
|
||||||
</ion-col>
|
|
||||||
</ion-row>
|
|
||||||
</ion-grid>
|
|
||||||
</ion-item>
|
|
||||||
<stapps-simple-card
|
|
||||||
*ngIf="item.description"
|
|
||||||
[title]="'description' | propertyNameTranslate: item | titlecase"
|
|
||||||
[content]="'description' | thingTranslate: item"
|
|
||||||
></stapps-simple-card>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
<stapps-origin-detail [origin]="item.origin"></stapps-origin-detail>
|
<stapps-origin-detail [origin]="item.origin"></stapps-origin-detail>
|
||||||
|
|
||||||
|
<ng-template #defaultContent>
|
||||||
|
<div [ngSwitch]="item.type">
|
||||||
|
<stapps-article-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'article'"
|
||||||
|
></stapps-article-detail-content>
|
||||||
|
<stapps-catalog-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'catalog'"
|
||||||
|
></stapps-catalog-detail-content>
|
||||||
|
<stapps-date-series-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'date series'"
|
||||||
|
></stapps-date-series-detail-content>
|
||||||
|
<stapps-dish-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'dish'"
|
||||||
|
></stapps-dish-detail-content>
|
||||||
|
<stapps-event-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'academic event'"
|
||||||
|
></stapps-event-detail-content>
|
||||||
|
<stapps-event-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'sport course'"
|
||||||
|
></stapps-event-detail-content>
|
||||||
|
<stapps-favorite-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'favorite'"
|
||||||
|
></stapps-favorite-detail-content>
|
||||||
|
<stapps-message-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'message'"
|
||||||
|
></stapps-message-detail-content>
|
||||||
|
<stapps-person-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'person'"
|
||||||
|
></stapps-person-detail-content>
|
||||||
|
<stapps-place-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'building'"
|
||||||
|
></stapps-place-detail-content>
|
||||||
|
<stapps-place-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'floor'"
|
||||||
|
></stapps-place-detail-content>
|
||||||
|
<stapps-place-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'point of interest'"
|
||||||
|
></stapps-place-detail-content>
|
||||||
|
<stapps-place-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'room'"
|
||||||
|
></stapps-place-detail-content>
|
||||||
|
<stapps-semester-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'semester'"
|
||||||
|
></stapps-semester-detail-content>
|
||||||
|
<stapps-video-detail-content
|
||||||
|
[item]="$any(item)"
|
||||||
|
*ngSwitchCase="'video'"
|
||||||
|
></stapps-video-detail-content>
|
||||||
|
<ng-container *ngSwitchDefault>
|
||||||
|
<ion-item class="ion-text-wrap" lines="inset">
|
||||||
|
<ion-thumbnail slot="start" class="ion-margin-end">
|
||||||
|
<ion-icon
|
||||||
|
color="medium"
|
||||||
|
[attr.name]="item.type | dataIcon"
|
||||||
|
></ion-icon>
|
||||||
|
</ion-thumbnail>
|
||||||
|
<ion-grid>
|
||||||
|
<ion-row>
|
||||||
|
<ion-col>
|
||||||
|
<div class="ion-text-wrap">
|
||||||
|
<h2 class="name">{{ item.name }}</h2>
|
||||||
|
<ion-note>{{ item.type }}</ion-note>
|
||||||
|
</div>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
</ion-item>
|
||||||
|
<stapps-simple-card
|
||||||
|
*ngIf="item.description"
|
||||||
|
[title]="$any('description' | propertyNameTranslate: item) | titlecase"
|
||||||
|
[content]="'description' | thingTranslate: item"
|
||||||
|
></stapps-simple-card>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018, 2019 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core';
|
import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
import {ActivatedRoute, RouterModule} from '@angular/router';
|
import {ActivatedRoute, RouterModule} from '@angular/router';
|
||||||
@@ -118,9 +119,10 @@ describe('DataDetailComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get a data item', () => {
|
it('should get a data item', () => {
|
||||||
comp.getItem(sampleThing.uid);
|
comp.getItem(sampleThing.uid, false);
|
||||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -128,6 +130,7 @@ describe('DataDetailComponent', () => {
|
|||||||
comp.ionViewWillEnter();
|
comp.ionViewWillEnter();
|
||||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -135,6 +138,7 @@ describe('DataDetailComponent', () => {
|
|||||||
await comp.refresh(refresher);
|
await comp.refresh(refresher);
|
||||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(refresher.complete).toHaveBeenCalled();
|
expect(refresher.complete).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018, 2019 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component} from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {IonRefresher} from '@ionic/angular';
|
import {IonRefresher, ViewWillEnter} from '@ionic/angular';
|
||||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
SCLanguageCode,
|
SCLanguageCode,
|
||||||
@@ -26,6 +33,13 @@ import {DataProvider, DataScope} from '../data.provider';
|
|||||||
import {FavoritesService} from '../../favorites/favorites.service';
|
import {FavoritesService} from '../../favorites/favorites.service';
|
||||||
import {take} from 'rxjs/operators';
|
import {take} from 'rxjs/operators';
|
||||||
import {Network} from '@capacitor/network';
|
import {Network} from '@capacitor/network';
|
||||||
|
import {DataListContext} from '../list/data-list.component';
|
||||||
|
|
||||||
|
export interface ExternalDataLoadEvent {
|
||||||
|
uid: SCUuid;
|
||||||
|
forceReload: boolean;
|
||||||
|
resolve: (item: SCThings | null | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Component to display an SCThing detailed
|
* A Component to display an SCThing detailed
|
||||||
@@ -35,7 +49,7 @@ import {Network} from '@capacitor/network';
|
|||||||
styleUrls: ['data-detail.scss'],
|
styleUrls: ['data-detail.scss'],
|
||||||
templateUrl: 'data-detail.html',
|
templateUrl: 'data-detail.html',
|
||||||
})
|
})
|
||||||
export class DataDetailComponent {
|
export class DataDetailComponent implements ViewWillEnter {
|
||||||
/**
|
/**
|
||||||
* The associated item
|
* The associated item
|
||||||
*
|
*
|
||||||
@@ -53,6 +67,25 @@ export class DataDetailComponent {
|
|||||||
*/
|
*/
|
||||||
isDisconnected: Promise<boolean>;
|
isDisconnected: Promise<boolean>;
|
||||||
|
|
||||||
|
@ContentChild(TemplateRef) contentTemplateRef: TemplateRef<
|
||||||
|
DataListContext<SCThings>
|
||||||
|
>;
|
||||||
|
|
||||||
|
@Input() externalData = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is kind of a stupid situation where we would
|
||||||
|
* like to use the default header in overriding elements
|
||||||
|
* such as the assessment detail page, however the ionic
|
||||||
|
* back button will not work if the header is in a subcomponent
|
||||||
|
* which then means we have to copy and paste the header from
|
||||||
|
* here into the overriding component.
|
||||||
|
*/
|
||||||
|
@Input() defaultHeader = true;
|
||||||
|
|
||||||
|
@Output() loadItem: EventEmitter<ExternalDataLoadEvent> =
|
||||||
|
new EventEmitter<ExternalDataLoadEvent>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type guard for SCSavableThing
|
* Type guard for SCSavableThing
|
||||||
*/
|
*/
|
||||||
@@ -90,11 +123,22 @@ export class DataDetailComponent {
|
|||||||
* Provides data item with given UID
|
* Provides data item with given UID
|
||||||
*
|
*
|
||||||
* @param uid Unique identifier of a thing
|
* @param uid Unique identifier of a thing
|
||||||
|
* @param forceReload Indicating whether cached data should be ignored
|
||||||
*/
|
*/
|
||||||
async getItem(uid: SCUuid) {
|
async getItem(uid: SCUuid, forceReload: boolean) {
|
||||||
try {
|
try {
|
||||||
const item = await this.dataProvider.get(uid, DataScope.Remote);
|
const item = await (this.externalData
|
||||||
this.item = DataDetailComponent.isSCSavableThing(item) ? item.data : item;
|
? new Promise<SCThings | null | undefined>(resolve =>
|
||||||
|
this.loadItem.emit({uid, forceReload, resolve}),
|
||||||
|
)
|
||||||
|
: this.dataProvider.get(uid, DataScope.Remote));
|
||||||
|
|
||||||
|
this.item = !item
|
||||||
|
? // eslint-disable-next-line unicorn/no-null
|
||||||
|
null
|
||||||
|
: DataDetailComponent.isSCSavableThing(item)
|
||||||
|
? item.data
|
||||||
|
: item;
|
||||||
} catch {
|
} catch {
|
||||||
// eslint-disable-next-line unicorn/no-null
|
// eslint-disable-next-line unicorn/no-null
|
||||||
this.item = null;
|
this.item = null;
|
||||||
@@ -106,7 +150,7 @@ export class DataDetailComponent {
|
|||||||
*/
|
*/
|
||||||
async ionViewWillEnter() {
|
async ionViewWillEnter() {
|
||||||
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
||||||
await this.getItem(uid ?? '');
|
await this.getItem(uid ?? '', false);
|
||||||
// fallback to the saved item (from favorites)
|
// fallback to the saved item (from favorites)
|
||||||
if (this.item === null) {
|
if (this.item === null) {
|
||||||
this.favoritesService
|
this.favoritesService
|
||||||
@@ -126,7 +170,7 @@ export class DataDetailComponent {
|
|||||||
* @param refresher Refresher component that triggers the update
|
* @param refresher Refresher component that triggers the update
|
||||||
*/
|
*/
|
||||||
async refresh(refresher: IonRefresher) {
|
async refresh(refresher: IonRefresher) {
|
||||||
await this.getItem(this.route.snapshot.paramMap.get('uid') ?? '');
|
await this.getItem(this.route.snapshot.paramMap.get('uid') ?? '', true);
|
||||||
await refresher.complete();
|
await refresher.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<ion-header>
|
<ion-header *ngIf="defaultHeader">
|
||||||
<ion-toolbar color="primary">
|
<ion-toolbar color="primary">
|
||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-back-button></ion-back-button>
|
<ion-back-button></ion-back-button>
|
||||||
@@ -8,11 +8,12 @@
|
|||||||
<ion-buttons slot="primary">
|
<ion-buttons slot="primary">
|
||||||
<stapps-favorite-button
|
<stapps-favorite-button
|
||||||
*ngIf="item"
|
*ngIf="item"
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
></stapps-favorite-button>
|
></stapps-favorite-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
<ng-content select="[header]"></ng-content>
|
||||||
<ion-content class="ion-no-padding">
|
<ion-content class="ion-no-padding">
|
||||||
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
|
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
|
||||||
<ion-refresher-content
|
<ion-refresher-content
|
||||||
@@ -44,7 +45,10 @@
|
|||||||
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
|
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
<stapps-data-detail-content [item]="item"></stapps-data-detail-content>
|
<stapps-data-detail-content
|
||||||
|
[item]="item"
|
||||||
|
[contentTemplateRef]="contentTemplateRef"
|
||||||
|
></stapps-data-detail-content>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018-2021 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input} from '@angular/core';
|
import {Component, ContentChild, Input, TemplateRef} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {DataRoutingService} from '../data-routing.service';
|
import {DataRoutingService} from '../data-routing.service';
|
||||||
|
import {DataListContext} from './data-list.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows data items in lists such es search result
|
* Shows data items in lists such es search result
|
||||||
@@ -35,6 +36,12 @@ export class DataListItemComponent {
|
|||||||
*/
|
*/
|
||||||
@Input() item: SCThings;
|
@Input() item: SCThings;
|
||||||
|
|
||||||
|
@Input() favoriteButton = true;
|
||||||
|
|
||||||
|
@ContentChild(TemplateRef) contentTemplateRef: TemplateRef<
|
||||||
|
DataListContext<SCThings>
|
||||||
|
>;
|
||||||
|
|
||||||
constructor(private readonly dataRoutingService: DataRoutingService) {}
|
constructor(private readonly dataRoutingService: DataRoutingService) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,66 +8,83 @@
|
|||||||
<ion-thumbnail slot="start" *ngIf="!hideThumbnail" class="ion-margin-end">
|
<ion-thumbnail slot="start" *ngIf="!hideThumbnail" class="ion-margin-end">
|
||||||
<ion-icon color="medium" [attr.name]="item.type | dataIcon"></ion-icon>
|
<ion-icon color="medium" [attr.name]="item.type | dataIcon"></ion-icon>
|
||||||
</ion-thumbnail>
|
</ion-thumbnail>
|
||||||
|
<ng-container *ngIf="contentTemplateRef; else defaultContent">
|
||||||
|
<ion-label class="ion-text-wrap" [ngSwitch]="true">
|
||||||
|
<div>
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="contentTemplateRef; context: {$implicit: item}"
|
||||||
|
></ng-container>
|
||||||
|
</div>
|
||||||
|
</ion-label>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<stapps-favorite-button
|
||||||
|
*ngIf="favoriteButton"
|
||||||
|
[item]="$any(item)"
|
||||||
|
></stapps-favorite-button>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ng-template #defaultContent>
|
||||||
<ion-label class="ion-text-wrap" [ngSwitch]="true">
|
<ion-label class="ion-text-wrap" [ngSwitch]="true">
|
||||||
<div>
|
<div>
|
||||||
<stapps-catalog-list-item
|
<stapps-catalog-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'catalog'"
|
*ngSwitchCase="item.type === 'catalog'"
|
||||||
></stapps-catalog-list-item>
|
></stapps-catalog-list-item>
|
||||||
<stapps-date-series-list-item
|
<stapps-date-series-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'date series'"
|
*ngSwitchCase="item.type === 'date series'"
|
||||||
></stapps-date-series-list-item>
|
></stapps-date-series-list-item>
|
||||||
<stapps-dish-list-item
|
<stapps-dish-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'dish'"
|
*ngSwitchCase="item.type === 'dish'"
|
||||||
></stapps-dish-list-item>
|
></stapps-dish-list-item>
|
||||||
<stapps-event-list-item
|
<stapps-event-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'academic event'"
|
*ngSwitchCase="item.type === 'academic event'"
|
||||||
></stapps-event-list-item>
|
></stapps-event-list-item>
|
||||||
<stapps-event-list-item
|
<stapps-event-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'sport course'"
|
*ngSwitchCase="item.type === 'sport course'"
|
||||||
></stapps-event-list-item>
|
></stapps-event-list-item>
|
||||||
<stapps-favorite-list-item
|
<stapps-favorite-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'favorite'"
|
*ngSwitchCase="item.type === 'favorite'"
|
||||||
></stapps-favorite-list-item>
|
></stapps-favorite-list-item>
|
||||||
<stapps-message-list-item
|
<stapps-message-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'message'"
|
*ngSwitchCase="item.type === 'message'"
|
||||||
></stapps-message-list-item>
|
></stapps-message-list-item>
|
||||||
<stapps-organization-list-item
|
<stapps-organization-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'organization'"
|
*ngSwitchCase="item.type === 'organization'"
|
||||||
></stapps-organization-list-item>
|
></stapps-organization-list-item>
|
||||||
<stapps-person-list-item
|
<stapps-person-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'person'"
|
*ngSwitchCase="item.type === 'person'"
|
||||||
></stapps-person-list-item>
|
></stapps-person-list-item>
|
||||||
<stapps-place-list-item
|
<stapps-place-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'building'"
|
*ngSwitchCase="item.type === 'building'"
|
||||||
></stapps-place-list-item>
|
></stapps-place-list-item>
|
||||||
<stapps-place-list-item
|
<stapps-place-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'floor'"
|
*ngSwitchCase="item.type === 'floor'"
|
||||||
></stapps-place-list-item>
|
></stapps-place-list-item>
|
||||||
<stapps-place-list-item
|
<stapps-place-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'point of interest'"
|
*ngSwitchCase="item.type === 'point of interest'"
|
||||||
></stapps-place-list-item>
|
></stapps-place-list-item>
|
||||||
<stapps-place-list-item
|
<stapps-place-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'room'"
|
*ngSwitchCase="item.type === 'room'"
|
||||||
></stapps-place-list-item>
|
></stapps-place-list-item>
|
||||||
<stapps-semester-list-item
|
<stapps-semester-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'semester'"
|
*ngSwitchCase="item.type === 'semester'"
|
||||||
></stapps-semester-list-item>
|
></stapps-semester-list-item>
|
||||||
<stapps-video-list-item
|
<stapps-video-list-item
|
||||||
[item]="item"
|
[item]="$any(item)"
|
||||||
*ngSwitchCase="item.type === 'video'"
|
*ngSwitchCase="item.type === 'video'"
|
||||||
></stapps-video-list-item>
|
></stapps-video-list-item>
|
||||||
<div *ngSwitchDefault>
|
<div *ngSwitchDefault>
|
||||||
@@ -87,5 +104,4 @@
|
|||||||
></stapps-action-chip-list>
|
></stapps-action-chip-list>
|
||||||
</div>
|
</div>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<stapps-favorite-button [item]="item"></stapps-favorite-button>
|
</ng-template>
|
||||||
</ion-item>
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
|
ContentChild,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
HostListener,
|
HostListener,
|
||||||
Input,
|
Input,
|
||||||
@@ -23,11 +24,16 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
|
TemplateRef,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
||||||
|
|
||||||
|
export interface DataListContext<T> {
|
||||||
|
$implicit: T;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the list of items
|
* Shows the list of items
|
||||||
*/
|
*/
|
||||||
@@ -47,6 +53,10 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
@Input() items?: SCThings[];
|
@Input() items?: SCThings[];
|
||||||
|
|
||||||
|
@ContentChild(TemplateRef) listItemTemplateRef: TemplateRef<
|
||||||
|
DataListContext<SCThings>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream of SCThings for virtual scroll to consume
|
* Stream of SCThings for virtual scroll to consume
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,12 +6,16 @@
|
|||||||
(scrolledIndexChange)="scrolled($event)"
|
(scrolledIndexChange)="scrolled($event)"
|
||||||
[style.display]="items && items.length ? 'block' : 'none'"
|
[style.display]="items && items.length ? 'block' : 'none'"
|
||||||
>
|
>
|
||||||
|
<ng-content select="[header]"></ng-content>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<stapps-data-list-item
|
<ng-container *cdkVirtualFor="let item of items; trackBy: identifyItem">
|
||||||
*cdkVirtualFor="let item of items; trackBy: identifyItem"
|
<ng-container
|
||||||
[item]="item"
|
*ngTemplateOutlet="
|
||||||
[hideThumbnail]="singleType"
|
listItemTemplateRef || defaultListItem;
|
||||||
></stapps-data-list-item>
|
context: {$implicit: item}
|
||||||
|
"
|
||||||
|
></ng-container>
|
||||||
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -28,3 +32,10 @@
|
|||||||
*ngFor="let skeleton of [].constructor(skeletonItems)"
|
*ngFor="let skeleton of [].constructor(skeletonItems)"
|
||||||
></stapps-skeleton-list-item>
|
></stapps-skeleton-list-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
|
<ng-template let-item #defaultListItem>
|
||||||
|
<stapps-data-list-item
|
||||||
|
[item]="item"
|
||||||
|
[hideThumbnail]="singleType"
|
||||||
|
></stapps-data-list-item>
|
||||||
|
</ng-template>
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
TemplateRef,
|
||||||
|
} from '@angular/core';
|
||||||
import {SCThings} from '@openstapps/core';
|
import {SCThings} from '@openstapps/core';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
import {DataRoutingService} from '../data-routing.service';
|
import {DataRoutingService} from '../data-routing.service';
|
||||||
|
import {DataListContext} from './data-list.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the list of items
|
* Shows the list of items
|
||||||
@@ -30,18 +38,24 @@ export class SimpleDataListComponent implements OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* All SCThings to display
|
* All SCThings to display
|
||||||
*/
|
*/
|
||||||
@Input() items?: SCThings[];
|
@Input() items?: Promise<SCThings[] | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether or not the list is to display SCThings of a single type
|
* Indicates whether or not the list is to display SCThings of a single type
|
||||||
*/
|
*/
|
||||||
@Input() singleType = false;
|
@Input() singleType = false;
|
||||||
|
|
||||||
|
@Input() autoRouting = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List header
|
* List header
|
||||||
*/
|
*/
|
||||||
@Input() listHeader?: string;
|
@Input() listHeader?: string;
|
||||||
|
|
||||||
|
@ContentChild(TemplateRef) listItemTemplateRef: TemplateRef<
|
||||||
|
DataListContext<SCThings>
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items that display the skeleton list
|
* Items that display the skeleton list
|
||||||
*/
|
*/
|
||||||
@@ -58,6 +72,7 @@ export class SimpleDataListComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (!this.autoRouting) return;
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
||||||
void this.router.navigate(['data-detail', item.uid]);
|
void this.router.navigate(['data-detail', item.uid]);
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
<ng-container *ngIf="items | async as items; else loading">
|
<ng-container *ngIf="items | async as items; else loading">
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ng-container *ngIf="!listHeader; else header"></ng-container>
|
<ng-container *ngIf="!listHeader; else header"></ng-container>
|
||||||
<stapps-data-list-item
|
<ng-container *ngFor="let item of items">
|
||||||
*ngFor="let item of items"
|
<ng-container
|
||||||
[item]="item"
|
*ngTemplateOutlet="
|
||||||
[hideThumbnail]="singleType"
|
listItemTemplateRef || defaultListItem;
|
||||||
></stapps-data-list-item>
|
context: {$implicit: item}
|
||||||
|
"
|
||||||
|
></ng-container>
|
||||||
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #loading>
|
<ng-template #loading>
|
||||||
@@ -26,3 +29,9 @@
|
|||||||
</ion-text>
|
</ion-text>
|
||||||
</ion-list-header>
|
</ion-list-header>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
<ng-template let-item #defaultListItem>
|
||||||
|
<stapps-data-list-item
|
||||||
|
[item]="item"
|
||||||
|
[hideThumbnail]="singleType"
|
||||||
|
></stapps-data-list-item>
|
||||||
|
</ng-template>
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018, 2019 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
import {ActivatedRoute, RouterModule} from '@angular/router';
|
import {ActivatedRoute, RouterModule} from '@angular/router';
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018, 2019 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
@@ -110,9 +112,10 @@ describe('HebisDetailComponent', () => {
|
|||||||
it('should create component', () => expect(comp).toBeDefined());
|
it('should create component', () => expect(comp).toBeDefined());
|
||||||
|
|
||||||
it('should get a data item', () => {
|
it('should get a data item', () => {
|
||||||
comp.getItem(sampleThing.uid);
|
comp.getItem(sampleThing.uid, false);
|
||||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -120,6 +123,7 @@ describe('HebisDetailComponent', () => {
|
|||||||
comp.ionViewWillEnter();
|
comp.ionViewWillEnter();
|
||||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,6 +131,7 @@ describe('HebisDetailComponent', () => {
|
|||||||
await comp.refresh(refresher);
|
await comp.refresh(refresher);
|
||||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||||
sampleThing.uid,
|
sampleThing.uid,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
expect(refresher.complete).toHaveBeenCalled();
|
expect(refresher.complete).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018-2022 StApps
|
* Copyright (C) 2022 StApps
|
||||||
* This program is free software: you can redistribute it and/or modify it
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
* Software Foundation, version 3.
|
* Software Foundation, version 3.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* You should have received a copy of the GNU General Public License along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
@@ -56,15 +56,16 @@ export class HebisDetailComponent extends DataDetailComponent {
|
|||||||
*/
|
*/
|
||||||
async ionViewWillEnter() {
|
async ionViewWillEnter() {
|
||||||
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
||||||
await this.getItem(uid ?? '');
|
await this.getItem(uid ?? '', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides data item with given UID
|
* Provides data item with given UID
|
||||||
*
|
*
|
||||||
* @param uid Unique identifier of a thing
|
* @param uid Unique identifier of a thing
|
||||||
|
* @param _forceReload Ignore any cached data
|
||||||
*/
|
*/
|
||||||
async getItem(uid: SCUuid) {
|
async getItem(uid: SCUuid, _forceReload: boolean) {
|
||||||
this.hebisDataProvider.hebisSearch({query: uid, page: 0}).then(result => {
|
this.hebisDataProvider.hebisSearch({query: uid, page: 0}).then(result => {
|
||||||
// eslint-disable-next-line unicorn/no-null
|
// eslint-disable-next-line unicorn/no-null
|
||||||
this.item = (result.data && result.data[0]) || null;
|
this.item = (result.data && result.data[0]) || null;
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 StApps
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
import {NgModule} from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
|
|||||||
@@ -356,6 +356,34 @@ export class MetersLocalizedPipe implements PipeTransform, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@Pipe({
|
||||||
|
name: 'isNaN',
|
||||||
|
pure: true,
|
||||||
|
})
|
||||||
|
export class IsNaNPipe implements PipeTransform {
|
||||||
|
transform(value: unknown): boolean {
|
||||||
|
return Number.isNaN(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
@Pipe({
|
||||||
|
name: 'isNumeric',
|
||||||
|
pure: true,
|
||||||
|
})
|
||||||
|
export class IsNumericPipe implements PipeTransform {
|
||||||
|
transform(value: unknown): boolean {
|
||||||
|
return !Number.isNaN(
|
||||||
|
typeof value === 'number'
|
||||||
|
? value
|
||||||
|
: typeof value === 'string'
|
||||||
|
? Number.parseFloat(value)
|
||||||
|
: Number.NaN,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'numberLocalized',
|
name: 'numberLocalized',
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
DurationLocalizedPipe,
|
DurationLocalizedPipe,
|
||||||
ToUnixPipe,
|
ToUnixPipe,
|
||||||
EntriesPipe,
|
EntriesPipe,
|
||||||
|
IsNaNPipe,
|
||||||
|
IsNumericPipe,
|
||||||
} from './common-string-pipes';
|
} from './common-string-pipes';
|
||||||
import {
|
import {
|
||||||
ThingTranslateDefaultParser,
|
ThingTranslateDefaultParser,
|
||||||
@@ -56,6 +58,8 @@ export interface ThingTranslateModuleConfig {
|
|||||||
SentenceCasePipe,
|
SentenceCasePipe,
|
||||||
ToUnixPipe,
|
ToUnixPipe,
|
||||||
EntriesPipe,
|
EntriesPipe,
|
||||||
|
IsNaNPipe,
|
||||||
|
IsNumericPipe,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ArrayJoinPipe,
|
ArrayJoinPipe,
|
||||||
@@ -71,6 +75,8 @@ export interface ThingTranslateModuleConfig {
|
|||||||
SentenceCasePipe,
|
SentenceCasePipe,
|
||||||
ToUnixPipe,
|
ToUnixPipe,
|
||||||
EntriesPipe,
|
EntriesPipe,
|
||||||
|
IsNaNPipe,
|
||||||
|
IsNumericPipe,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ThingTranslateModule {
|
export class ThingTranslateModule {
|
||||||
|
|||||||
@@ -15,6 +15,13 @@
|
|||||||
"UNKNOWN": "Unbekannter Fehler."
|
"UNKNOWN": "Unbekannter Fehler."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"assessments": {
|
||||||
|
"TITLE": "Noten",
|
||||||
|
"courseOfStudyAssessments": {
|
||||||
|
"PROGRESS": "Fortschritt",
|
||||||
|
"ASSESSMENTS": "Bewertungen"
|
||||||
|
}
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"messages": {
|
"messages": {
|
||||||
"default": {
|
"default": {
|
||||||
|
|||||||
@@ -15,6 +15,13 @@
|
|||||||
"UNKNOWN": "Unknown problem."
|
"UNKNOWN": "Unknown problem."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"assessments": {
|
||||||
|
"TITLE": "Grades",
|
||||||
|
"courseOfStudyAssessments": {
|
||||||
|
"PROGRESS": "Progress",
|
||||||
|
"ASSESSMENTS": "Assessments"
|
||||||
|
}
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"messages": {
|
"messages": {
|
||||||
"default": {
|
"default": {
|
||||||
|
|||||||
Reference in New Issue
Block a user