diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c41c391..682eae9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,12 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline #### Bug fixes * **(guided-tour)** : correction problème de chargement des traductions du tour guidé sur certains périphériques * **(statistics)** : suppression de l'utilisation du DeviceId pour le suivi des statistiques d'usage comme préconisé dans le RGPD. Utilisation d'une UID générée automatiquement par le client à la place. -* **(widgets)**: les widgets tiennent désormais compte de l'ionicon ou bien des icônes SVG renseignées du côté du CMS et les affichent à côté du titre de la Widget +* **(widgets)**: les widgets tiennent désormais compte de l'Ionicon ou bien des icônes SVG renseignées du côté du CMS et les affichent à côté du titre de la Widget + +#### New features +* **(schedule)** : Il est désormais possible de choisir entre 2 affichages pour la widget des prochains cours à venir : + * Liste verticale (list) + * Slider horizontal (slider) ### Backend #### Bug fixes diff --git a/dev/user-frontend-ionic/projects/schedule/README.md b/dev/user-frontend-ionic/projects/schedule/README.md index daecdddd..a532a2ee 100644 --- a/dev/user-frontend-ionic/projects/schedule/README.md +++ b/dev/user-frontend-ionic/projects/schedule/README.md @@ -2,36 +2,33 @@ Module permettant l'affichage des emplois du temps de l'utilisateur. -## Widgets -- `next-events` : Widget qui affiche les prochains évènements de l'emploi du temps. - ## Configuration -Exemple dans `app.module.ts` : +Exemple dans `environment.ts` : ```typescript - @NgModule({ - declarations: [AppComponent], - imports: [ - ScheduleModule.forRoot({ - nextEventsWidget: { - numberOfEventsLimit: 2, - numberOfDaysLimit: 7 - }, - previousWeeksInCache: 1, - nextWeeksInCache: 2, - managerRoles: ['role_1', 'role_2'] - }) - ] - }) +ScheduleModule.forRoot({ + nextEventsWidget: { + numberOfEventsLimit: 2, + numberOfDaysLimit: 7, + display: 'slider' + }, + previousWeeksInCache: 1, + nextWeeksInCache: 2, + managerRoles: ['role_1', 'role_2'] +}) ``` -### Configuration du widget "mes prochains cours" -- `nextEventsWidget.numberOfEventsLimit` : nombre maximum des prochains évènements à afficher. -- `nextEventsWidget.numberOfDaysLimit` : limite maximale du nombre de jours suivants le jour courant dans lesquels chercher les prochains évènements. - ### Configuration du cache - `previousWeeksInCache` : Le nombre de semaines précédentes à sauvegarder en cache. - `nextEventsWidget.numberOfDaysLimit` : Le nombre de semaines suivant à sauvegarder en cache. ### Configuration des gestionnaires d'emploi du temps - `managerRoles` : Liste des rôles permettant d'identifier l'utilisateur comme étant gestionnaire d'emploi du temps et lui permettant ainsi d'accéder à l'affichage d'un EDT pour une autre personne + +## Widgets +- `next-events` : Widget qui affiche les prochains évènements de l'emploi du temps. + +### Configuration du widget "mes prochains cours" +- `nextEventsWidget.numberOfEventsLimit` : nombre maximum des prochains évènements à afficher. +- `nextEventsWidget.numberOfDaysLimit` : limite maximale du nombre de jours suivants le jour courant dans lesquels chercher les prochains évènements. +- `nextEventsWidget.display` : ("list" | "slider") : choix de la vue, en liste ou en ligne diff --git a/dev/user-frontend-ionic/projects/schedule/src/lib/schedule.config.ts b/dev/user-frontend-ionic/projects/schedule/src/lib/schedule.config.ts index 14a5e306..3dab8af8 100644 --- a/dev/user-frontend-ionic/projects/schedule/src/lib/schedule.config.ts +++ b/dev/user-frontend-ionic/projects/schedule/src/lib/schedule.config.ts @@ -42,6 +42,7 @@ import { InjectionToken } from '@angular/core'; interface NextEventsWidgetConfig { numberOfEventsLimit: number; numberOfDaysLimit: number; + display: "list" | "slider"; } export interface ScheduleModuleConfig { diff --git a/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.html b/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.html index c57cd776..e60198db 100644 --- a/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.html +++ b/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.html @@ -37,65 +37,147 @@ ~ termes. --> - - - - - - - - - {{ "SCHEDULE.WIDGET.NEXT_EVENTS.NO_EVENTS" | translate }} - + + + + + + + - - - - {{ event.startDateTime | completeLocalDate }} - - - - -
-
- - - - {{event.startDateTime | localHour}} - {{event.endDateTime | localHour}} - - - - - - - {{event.course.label}} - - - - {{room.label}} - {{room.building}} + + + + + + {{ "SCHEDULE.WIDGET.NEXT_EVENTS.NO_EVENTS" | translate }} + + + + + + + + + + {{ event.startDateTime | completeLocalDate }} + + + + + +
+
+ + + + {{event.startDateTime | localHour}} - {{event.endDateTime | localHour}} + + + + + + + {{event.course.label}} + + + + + + + {{room.label}} - {{room.building}} + + + + + + + + + {{teacher.displayname}} + + + + + + + + + {{ event.course.url | truncate:35 }} + + + - - - {{teacher.displayname}} + +
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + {{ "SCHEDULE.WIDGET.NEXT_EVENTS.NO_EVENTS" | translate }} + + + + + + + + + {{event.startDateTime | localHour}} - {{event.endDateTime | localHour}} + + + {{completeLocalDate | slice: 0:completeLocalDate.indexOf(' ')}} + {{completeLocalDate | slice: completeLocalDate.indexOf(' ')}} + + + + {{event.course.label}} + + + + {{room.label}} - {{room.building}} + + + + {{teacher.displayname}} + + + + + + {{event.teachers[0].displayname}} (+ {{event.teachers.length - 1}} {{ "SCHEDULE.WIDGET.NEXT_EVENTS.LIMIT_TEACHERS" | translate }}) + + - - {{ event.course.url | truncate:35 }} + + {{ event.course.url | truncate:35 }} - - - - - - -
-
+ + + + + + + + + diff --git a/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.ts b/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.ts index 446c1f27..2b8b5bb5 100644 --- a/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.ts +++ b/dev/user-frontend-ionic/projects/schedule/src/lib/widgets/next-events/next-events.component.ts @@ -37,13 +37,14 @@ * termes. */ -import { AfterViewInit, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, Inject, Input, OnDestroy, TemplateRef, ViewChild } from '@angular/core'; import { CompleteLocalDatePipe, ThemeService } from '@multi/shared'; import { Observable, Subscription } from 'rxjs'; import { finalize, map, take } from 'rxjs/operators'; import { Event } from '../../schedule.repository'; import { ScheduleService } from '../../schedule.service'; import { NextEventsService } from './next-events.service'; +import { SCHEDULE_CONFIG, ScheduleModuleConfig } from '../../schedule.config'; @Component({ selector: 'app-schedule-widget-next-events', @@ -53,6 +54,8 @@ import { NextEventsService } from './next-events.service'; export class NextEventsComponent implements OnDestroy, AfterViewInit { @Input() widgetColor: string; + @ViewChild('list') list: TemplateRef; + @ViewChild('slider') slider: TemplateRef; public isLoading = false; public nextEvents$: Observable; @@ -65,7 +68,8 @@ export class NextEventsComponent implements OnDestroy, AfterViewInit { private scheduleService: ScheduleService, private completeLocalDatePipe: CompleteLocalDatePipe, private themeService: ThemeService, - private changeDetectorRef: ChangeDetectorRef + private changeDetectorRef: ChangeDetectorRef, + @Inject(SCHEDULE_CONFIG) private config: ScheduleModuleConfig ) { this.nextEvents$ = this.nextEventsService.getNextEvents$().pipe(); @@ -111,5 +115,9 @@ export class NextEventsComponent implements OnDestroy, AfterViewInit { ngOnDestroy() { this.nextEventsSubscription.unsubscribe(); } + + getTemplateRef(): TemplateRef { + return this[this?.config.nextEventsWidget.display]; + } } diff --git a/dev/user-frontend-ionic/src/environments/environment.ts.dist b/dev/user-frontend-ionic/src/environments/environment.ts.dist index a4ba19f7..85a833ab 100644 --- a/dev/user-frontend-ionic/src/environments/environment.ts.dist +++ b/dev/user-frontend-ionic/src/environments/environment.ts.dist @@ -108,7 +108,8 @@ export const environment: any = { ScheduleModule.forRoot({ nextEventsWidget: { numberOfEventsLimit: 2, - numberOfDaysLimit: 7 + numberOfDaysLimit: 7, + display: 'list' }, previousWeeksInCache: 1, nextWeeksInCache: 2, diff --git a/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/en.json b/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/en.json index 363668e0..f3d21886 100644 --- a/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/en.json +++ b/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/en.json @@ -42,7 +42,8 @@ }, "WIDGET": { "NEXT_EVENTS": { - "NO_EVENTS": "No courses soon" + "NO_EVENTS": "No courses soon", + "LIMIT_TEACHERS": "others" } } } diff --git a/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/fr.json b/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/fr.json index ab6357f4..3a060340 100644 --- a/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/fr.json +++ b/dev/user-frontend-ionic/src/theme/app-theme-dist/i18n/schedule/fr.json @@ -42,7 +42,8 @@ }, "WIDGET": { "NEXT_EVENTS": { - "NO_EVENTS": "Pas de cours prochainement" + "NO_EVENTS": "Pas de cours prochainement", + "LIMIT_TEACHERS": "autres" } } } diff --git a/dev/user-frontend-ionic/src/theme/app-theme-dist/styles/schedule/next-events.component.scss b/dev/user-frontend-ionic/src/theme/app-theme-dist/styles/schedule/next-events.component.scss index fd105f35..34b93e9a 100644 --- a/dev/user-frontend-ionic/src/theme/app-theme-dist/styles/schedule/next-events.component.scss +++ b/dev/user-frontend-ionic/src/theme/app-theme-dist/styles/schedule/next-events.component.scss @@ -37,79 +37,195 @@ * termes. */ -ion-card { - overflow: visible; - box-shadow: var(--app-border-no-box-shadow); - border-radius: var(--app-border-no-radius); - margin-top: 0px !important; - margin-bottom: 0px !important; - background: transparent; -} +.next-events-list-style { -.last-card { - padding-bottom: 0.8rem !important; -} + ion-card { + overflow: visible; + box-shadow: var(--app-border-no-box-shadow); + border-radius: var(--app-border-no-radius); + margin-top: 0 !important; + margin-bottom: 0 !important; + background: transparent; + } -.events-container { -padding-left: 1rem !important; -padding-bottom: 0rem !important; -} + .last-card { + padding-bottom: 0.8rem !important; + } -.dashed-line{ - position: absolute; - top: 0.77rem; - left: 0rem; - width: 2rem; - height: 100%; - - background-image: linear-gradient(rgb(255, 255, 255) 43%, rgba(255, 255, 255, 0) 0%); - background-position: left; - background-size: 0.13rem 0.9rem; - background-repeat: repeat-y; - overflow: visible; - z-index: 10 !important; -} + .events-container { + padding-left: 1rem !important; + padding-bottom: 0 !important; + } -.event-day { - position: relative; - padding-bottom: 0.5rem !important; - padding-top: 0.5rem !important; - padding-left: 0rem !important; - z-index: 12; -} + .dashed-line{ + position: absolute; + top: 0.77rem; + left: 0; + width: 2rem; + height: 100%; + background-image: linear-gradient(rgb(255, 255, 255) 43%, rgba(255, 255, 255, 0) 0%); + background-position: left; + background-size: 0.13rem 0.9rem; + background-repeat: repeat-y; + overflow: visible; + z-index: 10 !important; + } -ion-card-header { - padding-top: 0px; - padding-left: 1.2rem; - padding-bottom: 0px; -} + .event-day { + position: relative; + padding-bottom: 0.5rem !important; + padding-top: 0.5rem !important; + padding-left: 0 !important; + z-index: 12; + } -ion-card-content { - padding-left: 2.3rem; -} + ion-card-header { + padding-top: 0; + padding-left: 1.2rem; + padding-bottom: 0; + } -.circle-top-left { - position: absolute; - top: 0.30rem; - left: -0.18rem; - width: 0.5rem; - height: 0.5rem; - background-color: var(--ion-color-dark-contrast); - border-radius: 50%; -} + ion-card-content { + padding-left: 2.3rem; + } -.card-labels { - margin-right: 0.6rem; -} + .circle-top-left { + position: absolute; + top: 0.30rem; + left: -0.18rem; + width: 0.5rem; + height: 0.5rem; + background-color: var(--ion-color-dark-contrast); + border-radius: 50%; + } -.card-labels-icons { - margin-right: 0.3rem; -} + .card-labels { + margin-right: 0.6rem; + } + .card-labels-icons { + margin-right: 0.3rem; + } + + .light-font-color { + color: var(--app-font-color-for-dark-background-from-cms) !important; + } + + .dark-font-color { + color: var(--app-font-color-for-light-background-from-cms) !important; + } -.light-font-color{ - color: var(--app-font-color-for-dark-background-from-cms) !important; } -.dark-font-color{ - color: var(--app-font-color-for-light-background-from-cms) !important; +.next-events-slider-style { + display: block; + + ion-card { + scroll-snap-align: center; + } + + ion-card.slider-theme-color { + box-shadow: var(--app-border-no-box-shadow); + //border: var(--app-border-width-7) solid var(--app-border-color-primary); + background-color: var(--ion-color-tertiary); + } + + ion-card-content { + padding: 0.7rem 0; + height: 100%; + display: flex; + flex-direction: column; + } + + .empty-container { + padding-left: 1rem !important; + padding-bottom: 0 !important; + } + + .events-container { + padding: .5rem 0 0; + display: grid; + gap: 1rem; + grid-auto-flow: column; + grid-template-columns: 10px; + grid-auto-columns: 85%; + overflow-x: auto; + scrollbar-width: none; + scroll-snap-type: x mandatory; + overscroll-behavior-x: contain; + + &::-webkit-scrollbar { + display: none; + } + + &::before, + &::after { + content: ''; + width: 10px; + } + + .event-day-hour { + display: flex; + flex-direction: row; + border-left-width: 4px; + border-left-style: solid; + align-items: center; + flex-wrap: nowrap; + + .hour { + padding-left: 1rem; + display: block; + } + + .day { + padding-left: 1rem; + display: block; + + ion-text { + display: block; + } + } + } + } + + .card-labels { + margin-right: 0.6rem; + } + + .card-labels-icons { + margin-right: 0.3rem; + } + + .event-label { + padding: 1rem 1rem 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + } + + .event-additional { + display: flex; + padding: 0.2rem 0.5rem 0.2rem 2rem; + justify-content: flex-start; + flex-wrap: wrap; + flex-grow: 2; + align-items: center; + + ion-row:last-child { + margin-right: 0; + } + } + + .slider-font-color{ + color: var(--app-font-fix-light-color) !important; + } + + .light-font-color { + color: var(--app-font-color-for-dark-background-from-cms) !important; + } + + .dark-font-color { + color: var(--app-font-color-for-light-background-from-cms) !important; + } + }