diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3d737bab..f06b8652 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -39,6 +39,7 @@ import {LoggerModule, NGXLogger, NgxLoggerLevel} from 'ngx-logger'; import {environment} from '../environments/environment'; import {AppRoutingModule} from './app-routing.module'; import {AppComponent} from './app.component'; +import {CatalogModule} from './modules/catalog/catalog.module'; import {ConfigModule} from './modules/config/config.module'; import {ConfigProvider} from './modules/config/config.provider'; import {DataModule} from './modules/data/data.module'; @@ -137,6 +138,7 @@ const providers: Provider[] = [ AppRoutingModule, BrowserModule, BrowserAnimationsModule, + CatalogModule, CommonModule, ConfigModule, DataModule, diff --git a/src/app/modules/catalog/catalog.component.html b/src/app/modules/catalog/catalog.component.html new file mode 100644 index 00000000..8213e5fd --- /dev/null +++ b/src/app/modules/catalog/catalog.component.html @@ -0,0 +1,53 @@ + + + + + + + + {{ 'catalog.title' | translate | titlecase }} + + + + + + + {{ semester.acronym }} + + + + + +

{{ catalog.name }}

+

{{ catalog.acronym }}

+
+
+
+ + + + + + + +
+ + {{ 'catalog.detail.EMPTY_SEMESTER' | translate }} + +
+
+
+
+
diff --git a/src/app/modules/catalog/catalog.component.scss b/src/app/modules/catalog/catalog.component.scss new file mode 100644 index 00000000..f352ad01 --- /dev/null +++ b/src/app/modules/catalog/catalog.component.scss @@ -0,0 +1,9 @@ + +ion-segment-button { + max-width: 100%; + text-transform: none; +} + +.margin-top { + margin-top: 20vh; +} diff --git a/src/app/modules/catalog/catalog.component.ts b/src/app/modules/catalog/catalog.component.ts new file mode 100644 index 00000000..98468323 --- /dev/null +++ b/src/app/modules/catalog/catalog.component.ts @@ -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 . + */ +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 { + 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); + } +} diff --git a/src/app/modules/catalog/catalog.module.ts b/src/app/modules/catalog/catalog.module.ts new file mode 100644 index 00000000..e0e4ca3e --- /dev/null +++ b/src/app/modules/catalog/catalog.module.ts @@ -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 . + */ +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 {} diff --git a/src/app/modules/catalog/catalog.provider.ts b/src/app/modules/catalog/catalog.provider.ts new file mode 100644 index 00000000..5596bf1b --- /dev/null +++ b/src/app/modules/catalog/catalog.provider.ts @@ -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 . + */ +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 { + 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 { + 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'), + ); + } +} diff --git a/src/app/modules/data/data.module.ts b/src/app/modules/data/data.module.ts index 92a532d6..0e2a4612 100644 --- a/src/app/modules/data/data.module.ts +++ b/src/app/modules/data/data.module.ts @@ -81,6 +81,7 @@ import {OriginInListComponent} from './elements/origin-in-list.component'; import {CoordinatedSearchProvider} from './coordinated-search.provider'; import {Geolocation} from '@ionic-native/geolocation/ngx'; import {FavoriteButtonComponent} from './elements/favorite-button.component'; +import {SimpleDataListComponent} from './list/simple-data-list.component'; /** * Module for handling data @@ -135,8 +136,9 @@ import {FavoriteButtonComponent} from './elements/favorite-button.component'; SkeletonSimpleCardComponent, VideoDetailContentComponent, VideoListItemComponent, + SimpleDataListComponent, ], - entryComponents: [DataListComponent], + entryComponents: [DataListComponent, SimpleDataListComponent], imports: [ CommonModule, DataRoutingModule, @@ -177,6 +179,7 @@ import {FavoriteButtonComponent} from './elements/favorite-button.component'; SkeletonListItemComponent, SkeletonSimpleCardComponent, SearchPageComponent, + SimpleDataListComponent, ], }) export class DataModule {} diff --git a/src/app/modules/data/elements/long-inline-text.html b/src/app/modules/data/elements/long-inline-text.html index 089f104c..ab387b8d 100644 --- a/src/app/modules/data/elements/long-inline-text.html +++ b/src/app/modules/data/elements/long-inline-text.html @@ -1,3 +1,14 @@ -{{ text | slice: 0:size }}... + + {{ text | slice: 0:size }} + + + + {{ text | slice: 0:size * 2 }} + + … + + + + {{ text | slice: 0:size * 3 }} + + diff --git a/src/app/modules/data/elements/skeleton-list-item.component.ts b/src/app/modules/data/elements/skeleton-list-item.component.ts index 0fb5dbff..d257bc94 100644 --- a/src/app/modules/data/elements/skeleton-list-item.component.ts +++ b/src/app/modules/data/elements/skeleton-list-item.component.ts @@ -12,7 +12,7 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component} from '@angular/core'; +import {Component, Input} from '@angular/core'; /** * A placeholder to show when a list item is being loaded @@ -22,4 +22,6 @@ import {Component} from '@angular/core'; templateUrl: 'skeleton-list-item.html', styleUrls: ['skeleton-list-item.scss'], }) -export class SkeletonListItemComponent {} +export class SkeletonListItemComponent { + @Input() hideThumbnail = false; +} diff --git a/src/app/modules/data/elements/skeleton-list-item.html b/src/app/modules/data/elements/skeleton-list-item.html index a205b15d..8e65f754 100644 --- a/src/app/modules/data/elements/skeleton-list-item.html +++ b/src/app/modules/data/elements/skeleton-list-item.html @@ -1,5 +1,5 @@ - + diff --git a/src/app/modules/data/list/data-list.component.ts b/src/app/modules/data/list/data-list.component.ts index 5d88a4f5..d6085eb4 100644 --- a/src/app/modules/data/list/data-list.component.ts +++ b/src/app/modules/data/list/data-list.component.ts @@ -26,7 +26,6 @@ import { ViewChild, } from '@angular/core'; import {SCThings} from '@openstapps/core'; -import {ceil} from 'lodash-es'; import {BehaviorSubject, Observable, Subscription} from 'rxjs'; /** @@ -72,7 +71,7 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy { /** * Items that display the skeleton list */ - skeletonItems: number[]; + skeletonItems: number; /** * Array of all subscriptions to Observables @@ -84,12 +83,10 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy { /** * Calculate how many items would fill the screen */ - @HostListener('window.resize', ['$event']) + @HostListener('window:resize', ['$event']) calcSkeletonItems() { - const itemHeight = 122; - this.skeletonItems = Array.from({ - length: ceil(window.innerHeight / itemHeight), - }); + const itemHeight = 40; + this.skeletonItems = Math.ceil(window.innerHeight / itemHeight); } /** diff --git a/src/app/modules/data/list/data-list.html b/src/app/modules/data/list/data-list.html index 55510277..77493385 100644 --- a/src/app/modules/data/list/data-list.html +++ b/src/app/modules/data/list/data-list.html @@ -22,6 +22,7 @@ diff --git a/src/app/modules/data/list/data-list.scss b/src/app/modules/data/list/data-list.scss index 3965e303..ff579655 100644 --- a/src/app/modules/data/list/data-list.scss +++ b/src/app/modules/data/list/data-list.scss @@ -1,5 +1,6 @@ + cdk-virtual-scroll-viewport { - min-height: 100%; + height: 100%; width: 100%; } @@ -8,3 +9,7 @@ cdk-virtual-scroll-viewport { width: 100%; } } + +.virtual-scroll-expander { + clear: both; +} diff --git a/src/app/modules/data/list/search-page.component.ts b/src/app/modules/data/list/search-page.component.ts index 894176ec..12bd53e1 100644 --- a/src/app/modules/data/list/search-page.component.ts +++ b/src/app/modules/data/list/search-page.component.ts @@ -12,7 +12,7 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component, Input} from '@angular/core'; +import {Component, Input, OnInit, OnDestroy} from '@angular/core'; import {Router} from '@angular/router'; import {AlertController} from '@ionic/angular'; import { @@ -39,7 +39,7 @@ import {PositionService} from '../../map/position.service'; templateUrl: 'search-page.html', providers: [ContextMenuService], }) -export class SearchPageComponent { +export class SearchPageComponent implements OnInit, OnDestroy { /** * Api query filter */ @@ -126,9 +126,7 @@ export class SearchPageComponent { protected dataRoutingService: DataRoutingService, protected router: Router, protected positionService: PositionService, - ) { - this.initialize(); - } + ) {} /** * Fetches items with set query configuration @@ -230,7 +228,8 @@ export class SearchPageComponent { this.contextMenuService.updateContextFilter(facets); } - ionViewWillEnter() { + ngOnInit() { + this.initialize(); this.contextMenuService.setContextSort({ name: 'sort', reversed: false, @@ -290,7 +289,7 @@ export class SearchPageComponent { ); } - ionViewWillLeave() { + ngOnDestroy() { for (const subscription of this.subscriptions) { subscription.unsubscribe(); } diff --git a/src/app/modules/data/list/simple-data-list.component.ts b/src/app/modules/data/list/simple-data-list.component.ts new file mode 100644 index 00000000..8bf36e85 --- /dev/null +++ b/src/app/modules/data/list/simple-data-list.component.ts @@ -0,0 +1,76 @@ +/* + * 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 . + */ +import {Component, Input, OnDestroy, OnInit} from '@angular/core'; +import {SCThings} from '@openstapps/core'; +import {Subscription} from 'rxjs'; +import {Router} from '@angular/router'; +import {DataRoutingService} from '../data-routing.service'; + +/** + * Shows the list of items + */ +@Component({ + selector: 'stapps-simple-data-list', + templateUrl: 'simple-data-list.html', + styleUrls: ['simple-data-list.scss'], +}) +export class SimpleDataListComponent implements OnInit, OnDestroy { + /** + * All SCThings to display + */ + @Input() items?: SCThings[]; + + /** + * Indicates whether or not the list is to display SCThings of a single type + */ + @Input() singleType = false; + + /** + * List header + */ + @Input() listHeader?: string; + + /** + * Items that display the skeleton list + */ + skeletonItems = 6; + + /** + * Array of all subscriptions to Observables + */ + subscriptions: Subscription[] = []; + + constructor( + protected router: Router, + private readonly dataRoutingService: DataRoutingService, + ) {} + + ngOnInit(): void { + this.subscriptions.push( + this.dataRoutingService.itemSelectListener().subscribe(item => { + void this.router.navigate(['data-detail', item.uid]); + }), + ); + } + + /** + * Remove subscriptions when the component is removed + */ + ngOnDestroy() { + for (const sub of this.subscriptions) { + sub.unsubscribe(); + } + } +} diff --git a/src/app/modules/data/list/simple-data-list.html b/src/app/modules/data/list/simple-data-list.html new file mode 100644 index 00000000..20778d73 --- /dev/null +++ b/src/app/modules/data/list/simple-data-list.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + +

+ {{ listHeader }} +

+
+
+
diff --git a/src/app/modules/data/list/simple-data-list.scss b/src/app/modules/data/list/simple-data-list.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/data/types/catalog/catalog-detail-content.component.ts b/src/app/modules/data/types/catalog/catalog-detail-content.component.ts index 83b0106e..6cb85c9e 100644 --- a/src/app/modules/data/types/catalog/catalog-detail-content.component.ts +++ b/src/app/modules/data/types/catalog/catalog-detail-content.component.ts @@ -12,19 +12,159 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import {Component, Input} from '@angular/core'; -import {SCCatalog} from '@openstapps/core'; +import { + Component, + Input, + OnInit, + ViewChild, + ElementRef, + HostListener, + OnDestroy, + OnChanges, +} from '@angular/core'; +import {SCCatalog, SCSearchBooleanFilter} from '@openstapps/core'; +import {SearchPageComponent} from '../../list/search-page.component'; + +enum AccordionButtonState { + collapsed = 'chevron-down', + expanded = 'chevron-up', +} -/** - * TODO - */ @Component({ selector: 'stapps-catalog-detail-content', templateUrl: 'catalog-detail-content.html', + styleUrls: ['catalog-detail-content.scss'], }) -export class CatalogDetailContentComponent { +export class CatalogDetailContentComponent + extends SearchPageComponent + implements OnInit, OnDestroy, OnChanges +{ /** - * TODO + * SCCatalog to display */ @Input() item: SCCatalog; + + @ViewChild('accordionTextArea') accordionTextArea: ElementRef; + + buttonState = AccordionButtonState.collapsed; + + buttonShown = true; + + descriptionLinesShown: number; + + descriptionLinesTotal: number; + + descriptionPreviewLines = 3; + + descriptionLinesToDisplay = 0; + + ngOnInit() { + super.ngOnInit(); + if (this.item.description) { + this.descriptionLinesToDisplay = this.descriptionPreviewLines; + setTimeout(() => this.checkTextElipsis(), 0); + } + } + + ngOnChanges() { + this.checkTextElipsis(); + } + + initialize() { + this.pageSize = 100; + this.sortQuery = { + arguments: {field: 'name'}, + order: 'asc', + type: 'ducet', + }; + + const subCatalogFilter: SCSearchBooleanFilter = { + arguments: { + operation: 'and', + filters: [ + { + type: 'value', + arguments: { + field: 'type', + value: 'catalog', + }, + }, + { + type: 'value', + arguments: { + field: 'superCatalog.uid', + value: this.item.uid, + }, + }, + ], + }, + type: 'boolean', + }; + + const subEventsFilter: SCSearchBooleanFilter = { + arguments: { + operation: 'and', + filters: [ + { + type: 'value', + arguments: { + field: 'type', + value: 'academic event', + }, + }, + { + type: 'value', + arguments: { + field: 'catalogs.uid', + value: this.item.uid, + }, + }, + ], + }, + type: 'boolean', + }; + + this.forcedFilter = { + arguments: { + filters: [subCatalogFilter, subEventsFilter], + operation: 'or', + }, + type: 'boolean', + }; + } + + @HostListener('window:resize', ['$event']) + checkTextElipsis() { + if (typeof this.accordionTextArea === 'undefined') { + return; + } + const element = this.accordionTextArea.nativeElement as HTMLElement; + + const lineHeight = Number.parseInt( + getComputedStyle(element).getPropertyValue('line-height'), + ); + this.descriptionLinesTotal = element?.scrollHeight / lineHeight; + this.descriptionLinesShown = element?.offsetHeight / lineHeight; + if (this.buttonState === AccordionButtonState.expanded) { + this.descriptionLinesToDisplay = this.descriptionLinesTotal; + } + const isElipsed = element?.offsetHeight < element?.scrollHeight; + this.buttonShown = + (isElipsed && this.buttonState === AccordionButtonState.collapsed) || + (!isElipsed && this.buttonState === AccordionButtonState.expanded); + } + + toggleDescriptionAccordion() { + if (this.descriptionLinesToDisplay > 0) { + this.descriptionLinesToDisplay = + this.descriptionLinesToDisplay === this.descriptionPreviewLines + ? this.descriptionLinesTotal + : this.descriptionPreviewLines; + } + this.buttonState = + this.buttonState === AccordionButtonState.collapsed + ? AccordionButtonState.expanded + : AccordionButtonState.collapsed; + setTimeout(() => this.checkTextElipsis(), 0); + } } diff --git a/src/app/modules/data/types/catalog/catalog-detail-content.html b/src/app/modules/data/types/catalog/catalog-detail-content.html index 4d96e06b..70207f44 100644 --- a/src/app/modules/data/types/catalog/catalog-detail-content.html +++ b/src/app/modules/data/types/catalog/catalog-detail-content.html @@ -1,5 +1,33 @@ - + + +

+ {{ 'name' | thingTranslate: item }} +

+
+
+
+
+ {{ 'description' | thingTranslate: item }} +
+
+
+ + -
+ + + diff --git a/src/app/modules/data/types/catalog/catalog-detail-content.scss b/src/app/modules/data/types/catalog/catalog-detail-content.scss new file mode 100644 index 00000000..7443241a --- /dev/null +++ b/src/app/modules/data/types/catalog/catalog-detail-content.scss @@ -0,0 +1,6 @@ +.text-accordion { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; +} diff --git a/src/app/modules/data/types/catalog/catalog-list-item.html b/src/app/modules/data/types/catalog/catalog-list-item.html index 299ba58b..f6101702 100644 --- a/src/app/modules/data/types/catalog/catalog-list-item.html +++ b/src/app/modules/data/types/catalog/catalog-list-item.html @@ -2,14 +2,14 @@
-

{{ 'name' | thingTranslate: item }}

-

- -

-

{{ item.academicTerm.name }}

+ +

+ {{ 'name' | thingTranslate: item }} +

+
+ {{ + item.academicTerm.name + }}
diff --git a/src/app/modules/favorites/favorites-page.component.ts b/src/app/modules/favorites/favorites-page.component.ts index 05aa149c..419d0249 100644 --- a/src/app/modules/favorites/favorites-page.component.ts +++ b/src/app/modules/favorites/favorites-page.component.ts @@ -58,7 +58,6 @@ export class FavoritesPageComponent extends SearchPageComponent { } ionViewWillEnter() { - super.ionViewWillEnter(); this.subscriptions.push( this.favoritesService.favoritesChanged$.subscribe(_favoritesMap => { this.fetchAndUpdateItems(); diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index e33a1112..d5f55698 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -88,7 +88,7 @@ "page": { "TITLE": "Karte", "search": { - "PLACEHOLDER": "Finde Gebäude, Points of Interest, Mensen und Cafés ..." + "PLACEHOLDER": "Gebäude, Points of Interest, Mensen und mehr" }, "buttons": { "SHOW_LIST": "Liste ansehen", @@ -110,6 +110,12 @@ } } }, + "catalog": { + "title": "Vorlesungsverzeichnis", + "detail": { + "EMPTY_SEMESTER": "Keine Verzeichnisdaten für das ausgewählte Semester vorhanden" + } + }, "menu": { "context": { "title": "Kontext Menü", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 6bc1a2da..fd0e0129 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -88,7 +88,7 @@ "page": { "TITLE": "Map", "search": { - "PLACEHOLDER": "Find buildings, points of interests, canteens and cafes ..." + "PLACEHOLDER": "Buildings, points of interests, canteens and more" }, "buttons": { "SHOW_LIST": "Show list", @@ -110,6 +110,12 @@ } } }, + "catalog": { + "title": "course catalog", + "detail": { + "EMPTY_SEMESTER": "No catalog data available for selected semester" + } + }, "menu": { "context": { "title": "context menu",