mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 16:42:56 +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 {LibraryModule} from './modules/library/library.module';
|
||||
import {StorageProvider} from './modules/storage/storage.provider';
|
||||
import {AssessmentsModule} from './modules/assessments/assessments.module';
|
||||
|
||||
registerLocaleData(localeDe);
|
||||
|
||||
@@ -129,6 +130,7 @@ export function createTranslateLoader(http: HttpClient) {
|
||||
AboutModule,
|
||||
AppRoutingModule,
|
||||
AuthModule,
|
||||
AssessmentsModule,
|
||||
BackgroundModule,
|
||||
BrowserModule,
|
||||
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,
|
||||
_state: RouterStateSnapshot,
|
||||
) {
|
||||
if (route.queryParamMap.get('token')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.authHelper
|
||||
.getProvider(route.data.authProvider)
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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 {Component, Input, TemplateRef} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
import {DataListContext} from '../list/data-list.component';
|
||||
|
||||
/**
|
||||
* TODO
|
||||
@@ -28,4 +29,6 @@ export class DataDetailContentComponent {
|
||||
* TODO
|
||||
*/
|
||||
@Input() item: SCThings;
|
||||
|
||||
@Input() contentTemplateRef?: TemplateRef<DataListContext<SCThings>>;
|
||||
}
|
||||
|
||||
@@ -1,86 +1,99 @@
|
||||
<stapps-title-card [item]="item"> </stapps-title-card>
|
||||
<div [ngSwitch]="item.type">
|
||||
<stapps-article-detail-content
|
||||
[item]="item"
|
||||
*ngSwitchCase="'article'"
|
||||
></stapps-article-detail-content>
|
||||
<stapps-catalog-detail-content
|
||||
[item]="item"
|
||||
*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>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
contentTemplateRef || defaultContent;
|
||||
context: {$implicit: item}
|
||||
"
|
||||
>
|
||||
</ng-container>
|
||||
<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
|
||||
* 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 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 {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {ActivatedRoute, RouterModule} from '@angular/router';
|
||||
@@ -118,9 +119,10 @@ describe('DataDetailComponent', () => {
|
||||
});
|
||||
|
||||
it('should get a data item', () => {
|
||||
comp.getItem(sampleThing.uid);
|
||||
comp.getItem(sampleThing.uid, false);
|
||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -128,6 +130,7 @@ describe('DataDetailComponent', () => {
|
||||
comp.ionViewWillEnter();
|
||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -135,6 +138,7 @@ describe('DataDetailComponent', () => {
|
||||
await comp.refresh(refresher);
|
||||
expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
true,
|
||||
);
|
||||
expect(refresher.complete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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} from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {IonRefresher} from '@ionic/angular';
|
||||
import {IonRefresher, ViewWillEnter} from '@ionic/angular';
|
||||
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
|
||||
import {
|
||||
SCLanguageCode,
|
||||
@@ -26,6 +33,13 @@ import {DataProvider, DataScope} from '../data.provider';
|
||||
import {FavoritesService} from '../../favorites/favorites.service';
|
||||
import {take} from 'rxjs/operators';
|
||||
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
|
||||
@@ -35,7 +49,7 @@ import {Network} from '@capacitor/network';
|
||||
styleUrls: ['data-detail.scss'],
|
||||
templateUrl: 'data-detail.html',
|
||||
})
|
||||
export class DataDetailComponent {
|
||||
export class DataDetailComponent implements ViewWillEnter {
|
||||
/**
|
||||
* The associated item
|
||||
*
|
||||
@@ -53,6 +67,25 @@ export class DataDetailComponent {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -90,11 +123,22 @@ export class DataDetailComponent {
|
||||
* Provides data item with given UID
|
||||
*
|
||||
* @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 {
|
||||
const item = await this.dataProvider.get(uid, DataScope.Remote);
|
||||
this.item = DataDetailComponent.isSCSavableThing(item) ? item.data : item;
|
||||
const item = await (this.externalData
|
||||
? 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 {
|
||||
// eslint-disable-next-line unicorn/no-null
|
||||
this.item = null;
|
||||
@@ -106,7 +150,7 @@ export class DataDetailComponent {
|
||||
*/
|
||||
async ionViewWillEnter() {
|
||||
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
||||
await this.getItem(uid ?? '');
|
||||
await this.getItem(uid ?? '', false);
|
||||
// fallback to the saved item (from favorites)
|
||||
if (this.item === null) {
|
||||
this.favoritesService
|
||||
@@ -126,7 +170,7 @@ export class DataDetailComponent {
|
||||
* @param refresher Refresher component that triggers the update
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ion-header>
|
||||
<ion-header *ngIf="defaultHeader">
|
||||
<ion-toolbar color="primary">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
@@ -8,11 +8,12 @@
|
||||
<ion-buttons slot="primary">
|
||||
<stapps-favorite-button
|
||||
*ngIf="item"
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
></stapps-favorite-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ng-content select="[header]"></ng-content>
|
||||
<ion-content class="ion-no-padding">
|
||||
<ion-refresher slot="fixed" (ionRefresh)="refresh($event.target)">
|
||||
<ion-refresher-content
|
||||
@@ -44,7 +45,10 @@
|
||||
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
|
||||
</ng-container>
|
||||
<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>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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 {Component, ContentChild, Input, TemplateRef} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
import {DataRoutingService} from '../data-routing.service';
|
||||
import {DataListContext} from './data-list.component';
|
||||
|
||||
/**
|
||||
* Shows data items in lists such es search result
|
||||
@@ -35,6 +36,12 @@ export class DataListItemComponent {
|
||||
*/
|
||||
@Input() item: SCThings;
|
||||
|
||||
@Input() favoriteButton = true;
|
||||
|
||||
@ContentChild(TemplateRef) contentTemplateRef: TemplateRef<
|
||||
DataListContext<SCThings>
|
||||
>;
|
||||
|
||||
constructor(private readonly dataRoutingService: DataRoutingService) {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,66 +8,83 @@
|
||||
<ion-thumbnail slot="start" *ngIf="!hideThumbnail" class="ion-margin-end">
|
||||
<ion-icon color="medium" [attr.name]="item.type | dataIcon"></ion-icon>
|
||||
</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">
|
||||
<div>
|
||||
<stapps-catalog-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'catalog'"
|
||||
></stapps-catalog-list-item>
|
||||
<stapps-date-series-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'date series'"
|
||||
></stapps-date-series-list-item>
|
||||
<stapps-dish-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'dish'"
|
||||
></stapps-dish-list-item>
|
||||
<stapps-event-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'academic event'"
|
||||
></stapps-event-list-item>
|
||||
<stapps-event-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'sport course'"
|
||||
></stapps-event-list-item>
|
||||
<stapps-favorite-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'favorite'"
|
||||
></stapps-favorite-list-item>
|
||||
<stapps-message-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'message'"
|
||||
></stapps-message-list-item>
|
||||
<stapps-organization-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'organization'"
|
||||
></stapps-organization-list-item>
|
||||
<stapps-person-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'person'"
|
||||
></stapps-person-list-item>
|
||||
<stapps-place-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'building'"
|
||||
></stapps-place-list-item>
|
||||
<stapps-place-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'floor'"
|
||||
></stapps-place-list-item>
|
||||
<stapps-place-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'point of interest'"
|
||||
></stapps-place-list-item>
|
||||
<stapps-place-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'room'"
|
||||
></stapps-place-list-item>
|
||||
<stapps-semester-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'semester'"
|
||||
></stapps-semester-list-item>
|
||||
<stapps-video-list-item
|
||||
[item]="item"
|
||||
[item]="$any(item)"
|
||||
*ngSwitchCase="item.type === 'video'"
|
||||
></stapps-video-list-item>
|
||||
<div *ngSwitchDefault>
|
||||
@@ -87,5 +104,4 @@
|
||||
></stapps-action-chip-list>
|
||||
</div>
|
||||
</ion-label>
|
||||
<stapps-favorite-button [item]="item"></stapps-favorite-button>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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 {CdkVirtualScrollViewport} from '@angular/cdk/scrolling';
|
||||
import {
|
||||
Component,
|
||||
ContentChild,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
@@ -23,11 +24,16 @@ import {
|
||||
OnInit,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
import {BehaviorSubject, Observable, Subscription} from 'rxjs';
|
||||
|
||||
export interface DataListContext<T> {
|
||||
$implicit: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the list of items
|
||||
*/
|
||||
@@ -47,6 +53,10 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy {
|
||||
*/
|
||||
@Input() items?: SCThings[];
|
||||
|
||||
@ContentChild(TemplateRef) listItemTemplateRef: TemplateRef<
|
||||
DataListContext<SCThings>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Stream of SCThings for virtual scroll to consume
|
||||
*/
|
||||
|
||||
@@ -6,12 +6,16 @@
|
||||
(scrolledIndexChange)="scrolled($event)"
|
||||
[style.display]="items && items.length ? 'block' : 'none'"
|
||||
>
|
||||
<ng-content select="[header]"></ng-content>
|
||||
<ion-list>
|
||||
<stapps-data-list-item
|
||||
*cdkVirtualFor="let item of items; trackBy: identifyItem"
|
||||
[item]="item"
|
||||
[hideThumbnail]="singleType"
|
||||
></stapps-data-list-item>
|
||||
<ng-container *cdkVirtualFor="let item of items; trackBy: identifyItem">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
listItemTemplateRef || defaultListItem;
|
||||
context: {$implicit: item}
|
||||
"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</ng-container>
|
||||
@@ -28,3 +32,10 @@
|
||||
*ngFor="let skeleton of [].constructor(skeletonItems)"
|
||||
></stapps-skeleton-list-item>
|
||||
</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
|
||||
* 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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 {
|
||||
Component,
|
||||
ContentChild,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {Router} from '@angular/router';
|
||||
import {DataRoutingService} from '../data-routing.service';
|
||||
import {DataListContext} from './data-list.component';
|
||||
|
||||
/**
|
||||
* Shows the list of items
|
||||
@@ -30,18 +38,24 @@ export class SimpleDataListComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Input() singleType = false;
|
||||
|
||||
@Input() autoRouting = true;
|
||||
|
||||
/**
|
||||
* List header
|
||||
*/
|
||||
@Input() listHeader?: string;
|
||||
|
||||
@ContentChild(TemplateRef) listItemTemplateRef: TemplateRef<
|
||||
DataListContext<SCThings>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Items that display the skeleton list
|
||||
*/
|
||||
@@ -58,6 +72,7 @@ export class SimpleDataListComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.autoRouting) return;
|
||||
this.subscriptions.push(
|
||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
||||
void this.router.navigate(['data-detail', item.uid]);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<ng-container *ngIf="items | async as items; else loading">
|
||||
<ion-list>
|
||||
<ng-container *ngIf="!listHeader; else header"></ng-container>
|
||||
<stapps-data-list-item
|
||||
*ngFor="let item of items"
|
||||
[item]="item"
|
||||
[hideThumbnail]="singleType"
|
||||
></stapps-data-list-item>
|
||||
<ng-container *ngFor="let item of items">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="
|
||||
listItemTemplateRef || defaultListItem;
|
||||
context: {$implicit: item}
|
||||
"
|
||||
></ng-container>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
</ng-container>
|
||||
<ng-template #loading>
|
||||
@@ -26,3 +29,9 @@
|
||||
</ion-text>
|
||||
</ion-list-header>
|
||||
</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
|
||||
* 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 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 {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {ActivatedRoute, RouterModule} from '@angular/router';
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2018, 2019 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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 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 */
|
||||
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
@@ -110,9 +112,10 @@ describe('HebisDetailComponent', () => {
|
||||
it('should create component', () => expect(comp).toBeDefined());
|
||||
|
||||
it('should get a data item', () => {
|
||||
comp.getItem(sampleThing.uid);
|
||||
comp.getItem(sampleThing.uid, false);
|
||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -120,6 +123,7 @@ describe('HebisDetailComponent', () => {
|
||||
comp.ionViewWillEnter();
|
||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -127,6 +131,7 @@ describe('HebisDetailComponent', () => {
|
||||
await comp.refresh(refresher);
|
||||
expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith(
|
||||
sampleThing.uid,
|
||||
true,
|
||||
);
|
||||
expect(refresher.complete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2018-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.
|
||||
* 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 License for
|
||||
* more details.
|
||||
* 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/>.
|
||||
* 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} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
@@ -56,15 +56,16 @@ export class HebisDetailComponent extends DataDetailComponent {
|
||||
*/
|
||||
async ionViewWillEnter() {
|
||||
const uid = this.route.snapshot.paramMap.get('uid') || '';
|
||||
await this.getItem(uid ?? '');
|
||||
await this.getItem(uid ?? '', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data item with given UID
|
||||
*
|
||||
* @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 => {
|
||||
// eslint-disable-next-line unicorn/no-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 {CommonModule} from '@angular/common';
|
||||
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()
|
||||
@Pipe({
|
||||
name: 'numberLocalized',
|
||||
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
DurationLocalizedPipe,
|
||||
ToUnixPipe,
|
||||
EntriesPipe,
|
||||
IsNaNPipe,
|
||||
IsNumericPipe,
|
||||
} from './common-string-pipes';
|
||||
import {
|
||||
ThingTranslateDefaultParser,
|
||||
@@ -56,6 +58,8 @@ export interface ThingTranslateModuleConfig {
|
||||
SentenceCasePipe,
|
||||
ToUnixPipe,
|
||||
EntriesPipe,
|
||||
IsNaNPipe,
|
||||
IsNumericPipe,
|
||||
],
|
||||
exports: [
|
||||
ArrayJoinPipe,
|
||||
@@ -71,6 +75,8 @@ export interface ThingTranslateModuleConfig {
|
||||
SentenceCasePipe,
|
||||
ToUnixPipe,
|
||||
EntriesPipe,
|
||||
IsNaNPipe,
|
||||
IsNumericPipe,
|
||||
],
|
||||
})
|
||||
export class ThingTranslateModule {
|
||||
|
||||
Reference in New Issue
Block a user