mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-03-12 01:32:12 +00:00
feat: add catalog module
This commit is contained in:
committed by
Jovan Krunić
parent
e628f396e2
commit
03084b1c96
53
src/app/modules/catalog/catalog.component.html
Normal file
53
src/app/modules/catalog/catalog.component.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<!--TODO: read this from the config (menu item title)-->
|
||||
<ion-title>{{ 'catalog.title' | translate | titlecase }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-segment
|
||||
(ionChange)="segmentChanged($event)"
|
||||
[value]="selectedSemesterUID"
|
||||
mode="md"
|
||||
>
|
||||
<ion-segment-button
|
||||
*ngFor="let semester of availableSemesters"
|
||||
[value]="semester.uid"
|
||||
>
|
||||
<ion-label>{{ semester.acronym }}</ion-label>
|
||||
</ion-segment-button>
|
||||
</ion-segment>
|
||||
<ion-list *ngIf="catalogs && catalogs.length > 0">
|
||||
<ion-item
|
||||
*ngFor="let catalog of catalogs"
|
||||
button="true"
|
||||
lines="inset"
|
||||
(click)="notifySelect(catalog)"
|
||||
>
|
||||
<ion-label>
|
||||
<h2>{{ catalog.name }}</h2>
|
||||
<h3>{{ catalog.acronym }}</h3>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-list *ngIf="!catalogs">
|
||||
<stapps-skeleton-list-item *ngFor="let skeleton of [].constructor(10)">
|
||||
</stapps-skeleton-list-item>
|
||||
</ion-list>
|
||||
<ion-grid *ngIf="catalogs && catalogs.length === 0">
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<div class="ion-text-center margin-top">
|
||||
<ion-label>
|
||||
{{ 'catalog.detail.EMPTY_SEMESTER' | translate }}
|
||||
</ion-label>
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
||||
9
src/app/modules/catalog/catalog.component.scss
Normal file
9
src/app/modules/catalog/catalog.component.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
ion-segment-button {
|
||||
max-width: 100%;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.margin-top {
|
||||
margin-top: 20vh;
|
||||
}
|
||||
159
src/app/modules/catalog/catalog.component.ts
Normal file
159
src/app/modules/catalog/catalog.component.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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, OnInit, OnDestroy} from '@angular/core';
|
||||
import {Router, ActivatedRoute} from '@angular/router';
|
||||
import {SCCatalog, SCSemester} from '@openstapps/core';
|
||||
import moment from 'moment';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {CatalogProvider} from './catalog.provider';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import {Location} from '@angular/common';
|
||||
import {DataRoutingService} from '../data/data-routing.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-catalog',
|
||||
templateUrl: './catalog.component.html',
|
||||
styleUrls: ['./catalog.component.scss'],
|
||||
})
|
||||
export class CatalogComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* SCSemester to show
|
||||
*/
|
||||
activeSemester?: SCSemester;
|
||||
|
||||
/**
|
||||
* UID of the selected SCSemester
|
||||
*/
|
||||
selectedSemesterUID = '';
|
||||
|
||||
/**
|
||||
* Available SCSemesters
|
||||
*/
|
||||
availableSemesters: SCSemester[] = [];
|
||||
|
||||
/**
|
||||
* Catalogs (SCCatalog) to show
|
||||
*/
|
||||
catalogs: SCCatalog[] | undefined;
|
||||
|
||||
/**
|
||||
* Array of all subscriptions to Observables
|
||||
*/
|
||||
subscriptions: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* Supercatalog (SCCatalog) to refer to
|
||||
*/
|
||||
superCatalog: SCCatalog;
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly catalogProvider: CatalogProvider,
|
||||
private readonly dataRoutingService: DataRoutingService,
|
||||
private readonly logger: NGXLogger,
|
||||
protected router: Router,
|
||||
public location: Location,
|
||||
) {
|
||||
this.subscriptions.push(
|
||||
this.dataRoutingService.itemSelectListener().subscribe(item => {
|
||||
void this.router.navigate(['data-detail', item.uid]);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.selectedSemesterUID = this.route.snapshot.paramMap.get('uid') ?? '';
|
||||
void this.fetchCatalog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove subscriptions when the component is removed
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
for (const sub of this.subscriptions) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
async fetchCatalog() {
|
||||
try {
|
||||
if (this.availableSemesters.length === 0) {
|
||||
await this.fetchSemesters();
|
||||
}
|
||||
|
||||
const response = await this.catalogProvider.getCatalogsWith(
|
||||
0,
|
||||
this.superCatalog,
|
||||
this.activeSemester?.uid,
|
||||
);
|
||||
this.catalogs = (response.data as SCCatalog[]).sort((a, b) => {
|
||||
return new Intl.Collator('en', {
|
||||
numeric: true,
|
||||
sensitivity: 'accent',
|
||||
}).compare(a.name, b.name);
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(error.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchSemesters(): Promise<void> {
|
||||
const semesters = await this.catalogProvider.getRelevantSemesters();
|
||||
this.availableSemesters = semesters.slice(0, 3).reverse();
|
||||
|
||||
if (typeof this.activeSemester !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
const today = moment().startOf('day').toISOString();
|
||||
this.activeSemester = this.availableSemesters[0];
|
||||
this.activeSemester =
|
||||
this.selectedSemesterUID === ''
|
||||
? this.availableSemesters.find(
|
||||
semester => semester.startDate <= today && semester.endDate > today,
|
||||
)
|
||||
: this.availableSemesters.find(
|
||||
semester => semester.uid === this.selectedSemesterUID,
|
||||
);
|
||||
if (this.activeSemester && this.selectedSemesterUID === '') {
|
||||
this.selectedSemesterUID = this.activeSemester.uid;
|
||||
this.updateLocation(this.selectedSemesterUID);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
segmentChanged(event: any) {
|
||||
this.updateLocation(event.detail.value as string);
|
||||
this.activeSemester = this.availableSemesters.find(
|
||||
semester => semester.uid === (event.detail.value as string),
|
||||
);
|
||||
delete this.catalogs;
|
||||
void this.fetchCatalog();
|
||||
}
|
||||
|
||||
updateLocation(semesterUID: string) {
|
||||
const url = this.router
|
||||
.createUrlTree(['/catalog/', semesterUID])
|
||||
.toString();
|
||||
this.location.go(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit event that a catalog item was selected
|
||||
*/
|
||||
notifySelect(catalog: SCCatalog) {
|
||||
this.dataRoutingService.emitChildEvent(catalog);
|
||||
}
|
||||
}
|
||||
47
src/app/modules/catalog/catalog.module.ts
Normal file
47
src/app/modules/catalog/catalog.module.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 {CommonModule} from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {MomentModule} from 'ngx-moment';
|
||||
import {DataModule} from '../data/data.module';
|
||||
import {SettingsProvider} from '../settings/settings.provider';
|
||||
import {CatalogComponent} from './catalog.component';
|
||||
|
||||
const catalogRoutes: Routes = [
|
||||
{path: 'catalog', component: CatalogComponent},
|
||||
{path: 'catalog/:uid', component: CatalogComponent},
|
||||
];
|
||||
|
||||
/**
|
||||
* Catalog Module
|
||||
*/
|
||||
@NgModule({
|
||||
declarations: [CatalogComponent],
|
||||
imports: [
|
||||
IonicModule.forRoot(),
|
||||
FormsModule,
|
||||
TranslateModule.forChild(),
|
||||
RouterModule.forChild(catalogRoutes),
|
||||
CommonModule,
|
||||
MomentModule,
|
||||
DataModule,
|
||||
],
|
||||
providers: [SettingsProvider],
|
||||
})
|
||||
export class CatalogModule {}
|
||||
122
src/app/modules/catalog/catalog.provider.ts
Normal file
122
src/app/modules/catalog/catalog.provider.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 {
|
||||
SCCatalogWithoutReferences,
|
||||
SCSearchFilter,
|
||||
SCSearchResponse,
|
||||
SCSemester,
|
||||
SCThingType,
|
||||
} from '@openstapps/core';
|
||||
import {DataProvider} from '../data/data.provider';
|
||||
/**
|
||||
* Service for providing catalog and semester data
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CatalogProvider {
|
||||
constructor(private readonly dataProvider: DataProvider) {}
|
||||
|
||||
/**
|
||||
* Get news messages
|
||||
* TODO: make dates sortable on the backend side and then adjust this method
|
||||
*
|
||||
* @param offset TODO
|
||||
* @param superCatalog TODO
|
||||
* @param semesterUID TODO
|
||||
*/
|
||||
async getCatalogsWith(
|
||||
offset?: number,
|
||||
superCatalog?: SCCatalogWithoutReferences,
|
||||
semesterUID?: string,
|
||||
): Promise<SCSearchResponse> {
|
||||
const filters: SCSearchFilter[] = [
|
||||
{
|
||||
type: 'value',
|
||||
arguments: {
|
||||
field: 'type',
|
||||
value: 'catalog',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (typeof semesterUID === 'string') {
|
||||
filters.push({
|
||||
type: 'value',
|
||||
arguments: {
|
||||
field: 'academicTerm.uid',
|
||||
value: semesterUID,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof superCatalog?.uid === 'string') {
|
||||
filters.push({
|
||||
type: 'value',
|
||||
arguments: {
|
||||
field: 'superCatalog.uid',
|
||||
value: superCatalog.uid,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
filters.push({
|
||||
type: 'value',
|
||||
arguments: {
|
||||
field: 'level',
|
||||
value: '0',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return this.dataProvider.search({
|
||||
filter: {
|
||||
arguments: {
|
||||
operation: 'and',
|
||||
filters: filters,
|
||||
},
|
||||
type: 'boolean',
|
||||
},
|
||||
size: 40,
|
||||
from: offset,
|
||||
sort: [
|
||||
{
|
||||
type: 'ducet',
|
||||
order: 'asc',
|
||||
arguments: {
|
||||
field: 'name',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async getRelevantSemesters(): Promise<SCSemester[]> {
|
||||
const response = await this.dataProvider.search({
|
||||
filter: {
|
||||
arguments: {
|
||||
field: 'type',
|
||||
value: SCThingType.Semester,
|
||||
},
|
||||
type: 'value',
|
||||
},
|
||||
size: 10,
|
||||
});
|
||||
|
||||
return (response.data as SCSemester[]).sort((a, b) =>
|
||||
b.startDate.localeCompare(a.startDate, 'en'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user