change in app notification listing

This commit is contained in:
amentis 2024-01-10 14:24:32 +02:00
parent d0157fa0e5
commit c7ea2ce2db
26 changed files with 492 additions and 420 deletions

View File

@ -283,11 +283,11 @@ const appRoutes: Routes = [
},
},
{
path: 'inapp-notifications',
loadChildren: () => import('./ui/inapp-notification/inapp-notification.module').then(m => m.InAppNotificationModule),
path: 'mine-notifications',
loadChildren: () => import('./ui/inapp-notification/mine-inapp-notification.module').then(m => m.MineInAppNotificationModule),
data: {
authContext: {
permissions: [AppPermission.ViewInAppNotificationPage]
permissions: [AppPermission.ViewMineInAppNotificationPage]
},
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.INAPP-NOTIFICATIONS'

View File

@ -34,7 +34,7 @@ export enum AppPermission {
ViewTenantPage = 'ViewTenantPage',
ViewLanguagePage = "ViewLanguagePage",
ViewNotificationTemplatePage = "ViewNotificationTemplatePage",
ViewInAppNotificationPage = "ViewInAppNotificationPage",
ViewMineInAppNotificationPage = "ViewMineInAppNotificationPage",
//ReferenceType
BrowseReferenceType = "BrowseReferenceType",

View File

@ -57,11 +57,11 @@ export class InAppNotificationService {
catchError((error: any) => throwError(error)));
}
readAll(): Observable<string> {
readAll(): Observable<boolean> {
const url = `${this.apiBase}/read-all`;
return this.http
.post<string>(url, {}).pipe(
.post<boolean>(url, {}).pipe(
catchError((error: any) => throwError(error)));
}

View File

@ -12,7 +12,6 @@
</mat-card>
<div *ngIf="!isFromDialog" class="row editor-actions">
<div class="col"></div>
<div class="col-auto" *ngIf="!isDeleted && !isRead"><button mat-raised-button color="primary" (click)="markAsRead()" type="submit">{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-EDITOR.ACTIONS.READ' | translate}}</button></div>
<div class="col-auto" *ngIf="!this.isDeleted && !isNew"><button mat-raised-button color="primary" type="button" (click)="delete()">{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-EDITOR.ACTIONS.DELETE' | translate}}</button></div>
<div class="col-auto"><button mat-raised-button color="primary" (click)="cancel()" type="button">{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-EDITOR.ACTIONS.CANCEL' | translate}}</button></div>
</div>

View File

@ -71,6 +71,8 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
this.inappNotification = data;
this.isDeleted = this.inappNotification.isActive === IsActive.Inactive;
this.isRead = this.inappNotification.trackingState === NotificationInAppTracking.Delivered;
if(!this.isDeleted && !this.isRead) this.markAsRead()
},
error => this.onCallbackError(error)
);
@ -115,12 +117,12 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
}
public cancel(): void {
this.router.navigate(['/inapp-notifications']);
this.router.navigate(['/mine-notifications']);
}
onCallbackSuccess(data?: any): void {
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/inapp-notifications']);
this.router.navigate(['/mine-notifications']);
}
onCallbackError(errorResponse: HttpErrorResponse) {

View File

@ -6,11 +6,11 @@
<span mat-line class="secondary-text">{{ inappNotification.createdAt | date : 'short'}}</span>
<mat-divider inset *ngIf="!last"></mat-divider>
</a>
<mat-list-item class = "mat-list-item-content" *ngIf="authService.hasPermission(authService.permissionEnum.ViewInAppNotificationPage)">
<mat-list-item class = "mat-list-item-content" *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
<a (click)="goToNotifications()">{{'NAV-BAR.INAPP-NOTIFICATIONS'
| translate}}</a>
</mat-list-item>
<mat-list-item class = "mat-list-item-content" *ngIf="authService.hasPermission(authService.permissionEnum.ViewInAppNotificationPage)">
<mat-list-item class = "mat-list-item-content" *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
<a (click)="readAllNotifications()">{{'NAV-BAR.READ-ALL-INAPP-NOTIFICATIONS'
| translate}}</a>
</mat-list-item>

View File

@ -14,16 +14,16 @@ import { takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Component({
selector: 'app-inapp-notification-listing-dialog',
templateUrl: './inapp-notification-listing-dialog.component.html',
styleUrls: ['./inapp-notification-listing-dialog.component.scss']
selector: 'app-mine-inapp-notification-listing-dialog',
templateUrl: './mine-inapp-notification-listing-dialog.component.html',
styleUrls: ['./mine-inapp-notification-listing-dialog.component.scss']
})
export class InAppNotificationListingDialogComponent extends BaseComponent implements OnInit {
export class MineInAppNotificationListingDialogComponent extends BaseComponent implements OnInit {
public inappNotifications = new Array<InAppNotification>();
public notificationInAppTrackingEnum = NotificationInAppTracking;
constructor(
public dialogRef: MatDialogRef<InAppNotificationListingDialogComponent>,
public dialogRef: MatDialogRef<MineInAppNotificationListingDialogComponent>,
@Inject(MAT_DIALOG_DATA) public dialogData: any,
private inappNotificationService: InAppNotificationService,
private router: Router,
@ -68,21 +68,21 @@ export class InAppNotificationListingDialogComponent extends BaseComponent imple
.subscribe(
data => {
this.dialogRef.close();
this.router.navigate(['/inapp-notifications/dialog/' + item.id]);
this.router.navigate(['/mine-notifications/dialog/' + item.id]);
},
error => {
this.dialogRef.close();
this.router.navigate(['/inapp-notifications/dialog/' + item.id]);
this.router.navigate(['/mine-notifications/dialog/' + item.id]);
},
);
} else {
this.dialogRef.close();
this.router.navigate(['/inapp-notifications/dialog/' + item.id]);
this.router.navigate(['/mine-notifications/dialog/' + item.id]);
}
}
goToNotifications() {
this.router.navigate(['/inapp-notifications']);
this.router.navigate(['/mine-notifications']);
this.dialogRef.close();
}

View File

@ -1,30 +0,0 @@
<div class="inapp-notification-listing-filters">
<mat-expansion-panel class="expansion-panel" [(expanded)]="panelExpanded" [disabled]="true">
<mat-expansion-panel-header collapsedHeight="auto" expandedHeight="auto">
<div class="row w-100">
<app-text-filter class="col-auto" [(value)]="filter.like" (valueChange)="onFilterChange()"></app-text-filter>
<div class="filter-actions col-auto ml-auto">
<button mat-button color="primary" (click)="panelExpanded = !panelExpanded" [disableRipple]="true">
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.MORE-FILTERS' | translate}}
<mat-icon *ngIf="panelExpanded">keyboard_arrow_up</mat-icon>
<mat-icon *ngIf="!panelExpanded">keyboard_arrow_down</mat-icon>
</button>
</div>
</div>
</mat-expansion-panel-header>
<div class="row">
<mat-form-field class="col-md-3">
<mat-label>{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.TRACKING-STATE' | translate}}</mat-label>
<mat-select [(ngModel)]="trackingState">
<mat-option *ngIf="trackingState !== null"></mat-option>
<mat-option *ngFor="let trackingState of trackingStates" [value]="trackingState">{{enumUtils.toNotificationInAppTrackingString(trackingState)}}</mat-option>
</mat-select>
</mat-form-field>
<div class="col-md-3 d-flex justify-content-center align-items-center">
<mat-slide-toggle [(ngModel)]="isActive" class="toggle col-auto w-100">
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.IS-ACTIVE' | translate}}
</mat-slide-toggle>
</div>
</div>
</mat-expansion-panel>
</div>

View File

@ -1,22 +0,0 @@
.inapp-notification-listing-filters {
margin: 1em 0;
.filter-actions {
display: flex;
}
.toggle {
font-weight: 400;
height: 49px;
min-height: 53.59px;
background-color: white;
border-radius: 0.4em;
border: 1px solid #e0e0e0;
}
}
:host ::ng-deep .mat-form-field-wrapper {
padding-bottom: 0;
margin: 1em 0 !important;
}

View File

@ -1,73 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, OnChanges } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { NotificationInAppTracking } from '@app/core/common/enum/notification-inapp-tracking.enum';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { InAppNotificationFilter } from '@app/core/query/inapp-notification.lookup';
@Component({
selector: 'app-inapp-notification-listing-filters',
templateUrl: './inapp-notification-listing-filters.component.html',
styleUrls: ['./inapp-notification-listing-filters.component.scss']
})
export class InAppNotificationListingFiltersComponent extends BaseComponent implements OnInit, OnChanges {
@Input() filter: InAppNotificationFilter;
@Output() filterChange = new EventEmitter<InAppNotificationFilter>();
panelExpanded = false;
trackingStates: NotificationInAppTracking[] = this.enumUtils.getEnumValues(NotificationInAppTracking);
constructor(
public enumUtils: EnumUtils
) { super(); }
ngOnInit() {
this.panelExpanded = !this.areHiddenFieldsEmpty();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['filter']) { this.panelExpanded = !this.areHiddenFieldsEmpty(); }
}
onFilterChange() {
this.filterChange.emit(this.filter);
}
private areHiddenFieldsEmpty(): boolean {
return (!this.filter.isActive || this.filter.isActive.length === 0 || (this.filter.isActive.length === 1 && this.filter.isActive[0] === IsActive.Active));
}
//
// Filter getters / setters
// Implement here any custom logic regarding how these fields are applied to the lookup.
//
get like(): string {
return this.filter.like;
}
set like(value: string) {
this.filter.like = value;
this.filterChange.emit(this.filter);
}
get isActive(): boolean {
return this.filter.isActive ? this.filter.isActive.includes(IsActive.Inactive) : true;
}
set isActive(value: boolean) {
this.filter.isActive = value ? [IsActive.Active, IsActive.Inactive] : [IsActive.Active];
this.filterChange.emit(this.filter);
}
get trackingState(): NotificationInAppTracking[] {
return this.filter.trackingState ? this.filter.trackingState : null;
}
set trackingState(value: NotificationInAppTracking[]) {
// this.filter.trackingState = value && value.length > 0 ? value : null;
if (value[0] == 0){
this.filter.trackingState = [NotificationInAppTracking.Stored];
} else if(value[0] == 1) {
this.filter.trackingState = [NotificationInAppTracking.Delivered];
} else {
this.filter.trackingState = null;
}
this.filterChange.emit(this.filter);
}
}

View File

@ -0,0 +1,40 @@
<div class="d-flex align-items-center gap-1-rem">
<button mat-flat-button [matMenuTriggerFor]="filterMenu" #filterMenuTrigger="matMenuTrigger" (click)="updateFilters()" class="filter-button">
<mat-icon aria-hidden="false" [matBadgeHidden]="!appliedFilterCount" [matBadge]="appliedFilterCount" matBadgeColor="warn" matBadgeSize="small">filter_alt</mat-icon>
{{'COMMONS.LISTING-COMPONENT.SEARCH-FILTER-BTN' | translate}}
</button>
<mat-menu #filterMenu>
<div class="p-3" (click)="$event?.stopPropagation?.()">
<div class="search-listing-filters-container">
<div class="d-flex align-items-center justify-content-between">
<h4>{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.TITLE' | translate}}</h4>
<button color="accent" mat-button (click)="clearFilters()">
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
</button>
</div>
<div>
<mat-label>{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.TRACKING-STATE' | translate}}
<mat-select multiple [(ngModel)]="internalFilters.trackingState">
<mat-option *ngFor="let state of notificationInAppTrackingEnumValues" [value]="state">{{enumUtils.toNotificationInAppTrackingString(state)}}</mat-option>
</mat-select>
</mat-label>
</div>
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
</button>
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.APPLY-FILTERS' | translate}}
</button>
</div>
</div>
</div>
</mat-menu>
<app-expandable-search-field [(value)]=internalFilters.like (valueChange)="onSearchTermChange($event)" />
</div>

View File

@ -0,0 +1,20 @@
::ng-deep.mat-mdc-menu-panel {
max-width: 100% !important;
height: 100% !important;
}
:host::ng-deep.mat-mdc-menu-content:not(:empty) {
padding-top: 0 !important;
}
.filter-button{
padding-top: .6rem;
padding-bottom: .6rem;
// .mat-icon{
// font-size: 1.5em;
// width: 1.2em;
// height: 1.2em;
// }
}

View File

@ -0,0 +1,93 @@
import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges, OnChanges } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { NotificationInAppTracking } from '@app/core/common/enum/notification-inapp-tracking.enum';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { InAppNotificationFilter } from '@app/core/query/inapp-notification.lookup';
import { nameof } from 'ts-simple-nameof';
@Component({
selector: 'app-mine-inapp-notification-listing-filters',
templateUrl: './mine-inapp-notification-listing-filters.component.html',
styleUrls: ['./mine-inapp-notification-listing-filters.component.scss']
})
export class MineInAppNotificationListingFiltersComponent extends BaseComponent implements OnInit, OnChanges {
@Input() readonly filter: InAppNotificationFilter;
@Output() filterChange = new EventEmitter<InAppNotificationFilter>();
notificationInAppTrackingEnumValues = this.enumUtils.getEnumValues<NotificationInAppTracking>(NotificationInAppTracking);
// * State
internalFilters: InAppNotificationListingFilters = this._getEmptyFilters();
protected appliedFilterCount: number = 0;
constructor(
public enumUtils: EnumUtils,
) { super(); }
ngOnInit() {
}
ngOnChanges(changes: SimpleChanges): void {
const filterChange = changes[nameof<MineInAppNotificationListingFiltersComponent>(x => x.filter)]?.currentValue as InAppNotificationFilter;
if (filterChange) {
this.updateFilters()
}
}
onSearchTermChange(searchTerm: string): void {
this.applyFilters()
}
protected updateFilters(): void {
this.internalFilters = this._parseToInternalFilters(this.filter);
this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters);
}
protected applyFilters(): void {
const { like, trackingState } = this.internalFilters ?? {}
this.filterChange.emit({
...this.filter,
like,
trackingState
})
}
private _parseToInternalFilters(inputFilter: InAppNotificationFilter): InAppNotificationListingFilters {
if (!inputFilter) {
return this._getEmptyFilters();
}
let { like, trackingState } = inputFilter;
return {
like: like,
trackingState: trackingState
}
}
private _getEmptyFilters(): InAppNotificationListingFilters {
return {
like: null,
trackingState: null
}
}
private _computeAppliedFilters(filters: InAppNotificationListingFilters): number {
let count = 0;
return count;
}
clearFilters() {
this.internalFilters = this._getEmptyFilters();
}
}
interface InAppNotificationListingFilters {
like: string;
trackingState: NotificationInAppTracking[];
}

View File

@ -1,52 +0,0 @@
import { Serializable } from '@common/types/json/serializable';
import { Lookup } from '@common/model/lookup';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { InAppNotification } from '@app/core/model/inapp-notification/inapp-notification.model';
import { InAppNotificationLookup } from '@app/core/query/inapp-notification.lookup';
import { UserSettingsInformation, UserSettingsLookupBuilder } from '@app/core/model/user-settings/user-settings.model';
import { nameof } from 'ts-simple-nameof';
export class InAppNotificationListingUserSettings implements Serializable<InAppNotificationListingUserSettings>, UserSettingsLookupBuilder<InAppNotificationLookup> {
private like: string;
private isActive: IsActive[] = [IsActive.Active];
private order: Lookup.Ordering = { items: [nameof<InAppNotification>(x => x.createdAt)] };
private project: Lookup.FieldDirectives = {
fields: [
nameof<InAppNotification>(x => x.id),
nameof<InAppNotification>(x => x.subject),
nameof<InAppNotification>(x => x.trackingState),
nameof<InAppNotification>(x => x.createdAt)
]
};
static getUserSettingsInformation(): UserSettingsInformation<InAppNotificationListingUserSettings> {
return {
key: 'InAppNotificationListingUserSettings',
type: InAppNotificationListingUserSettings
};
}
public fromJSONObject(item: any): InAppNotificationListingUserSettings {
this.like = item.like;
this.isActive = item.isActive;
this.order = item.order;
this.project = item.project;
return this;
}
public update(lookup: InAppNotificationLookup) {
this.like = lookup.like;
this.isActive = lookup.isActive;
this.order = lookup.order;
this.project = lookup.project;
}
public apply(lookup: InAppNotificationLookup): InAppNotificationLookup {
lookup.like = this.like;
lookup.isActive = this.isActive;
lookup.order = this.order;
lookup.project = this.project;
return lookup;
}
}

View File

@ -1,46 +0,0 @@
<div class="row inapp-notification-listing">
<div class="col-12">
<div class="row page-title">
<div class="col-auto pl-5 pr-0 align-self-center">
<i class="fa fa-2x fa-envelope-open-o accent-color"></i>
</div>
<!-- <div class="col-auto ml-auto mt-3 align-self-center">
<button mat-raised-button color="accent" *ngIf="authService.hasAnyPermission([authService.permissionEnum.EditDocument, authService.permissionEnum.DeferredAffiliation])" [routerLink]=" ['./new'] ">
<mat-icon class="mb-1 mr-2">add</mat-icon>{{'COMMONS.ACTIONS.NEW' | translate}}
</button>
</div> -->
<div class="col-auto p-0 user-settings-selector">
<app-user-settings-selector [key]="userSettingsKey" [lookup]="lookup" (onSettingSelected)="changeSetting($event)" [autoSelectUserSettings]="autoSelectUserSettings"></app-user-settings-selector>
</div>
</div>
<div class="row toggle-row">
<div class="col-auto align-self-center view-mode-toggle-container">
<button mat-icon-button class="preview-btn" [ngClass]="{'preview-btn-active': isPreviewList}" (click)="togglePreviewMode(true)" matTooltip="{{'APP.DOCUMENT-LISTING.PREVIEW.LIST' | translate}}">
<mat-icon>view_module</mat-icon>
</button>
<button mat-icon-button class="preview-btn" [ngClass]="{'preview-btn-active': !isPreviewList}" (click)="togglePreviewMode(false)" matTooltip="{{'APP.DOCUMENT-LISTING.PREVIEW.TABLE' | translate}}">
<mat-icon>table_chart</mat-icon>
</button>
</div>
</div>
<div class="row">
<div class="col-12 filter-row">
<app-inapp-notification-listing-filters [(filter)]="lookup" (filterChange)="filterChanged($event)"></app-inapp-notification-listing-filters>
</div>
</div>
<div class="row">
<div class="col-md-12 col-center-align">
<app-hybrid-listing [rows]="gridRows" [columns]="gridColumns" [visibleColumns]="visibleColumns" [count]="totalElements" [offset]="currentPageNumber" [limit]="lookup.page.size" [defaultSort]="lookup.order?.items" [externalSorting]="true" (rowActivated)="onRowActivated($event)" (pageLoad)="onPageLoad($event)" (columnSort)="onColumnSort($event)" (columnsChanged)="onColumnsChanged($event)">
</app-hybrid-listing>
<ng-template #readColumnTemplate ngx-datatable-cell-template let-row="row">
<mat-icon *ngIf="row.trackingState === notificationInAppTrackingEnum.Delivered" mat-list-icon>drafts</mat-icon>
<mat-icon *ngIf="row.trackingState === notificationInAppTrackingEnum.Stored" mat-list-icon>mail</mat-icon>
</ng-template>
</div>
</div>
</div>
</div>

View File

@ -1,28 +0,0 @@
.inapp-notification-listing {
// padding-top: 1em;
.toggle-row {
min-height: 56px;
}
.preview-btn {
color: rgb(193, 202, 211);
}
.preview-btn-active {
color: black;
}
.view-mode-toggle-container {
padding-left: 30px;
}
.mat-fab-bottom-right {
top: auto !important;
right: 20px !important;
bottom: 10px !important;
left: auto !important;
position: fixed !important;
}
}

View File

@ -1,116 +0,0 @@
import { Component, OnInit, TemplateRef, ViewChild, EventEmitter, Output, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '@app/core/services/auth/auth.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseListingComponent } from '@common/base/base-listing-component';
import { PipeService } from '@common/formatting/pipe.service';
import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { ColumnsChangedEvent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { InAppNotification } from '@app/core/model/inapp-notification/inapp-notification.model';
import { InAppNotificationLookup } from '@app/core/query/inapp-notification.lookup';
import { InAppNotificationService } from '@app/core/services/inapp-notification/inapp-notification.service';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { nameof } from 'ts-simple-nameof';
import { NotificationInAppTrackingTypePipe } from '@common/formatting/pipes/notification-inapp-tracking-type.pipe';
import { NotificationInAppTracking } from '@app/core/common/enum/notification-inapp-tracking.enum';
import { Observable } from 'rxjs';
import { QueryResult } from '@common/model/query-result';
@Component({
selector: 'app-inapp-notification-listing',
templateUrl: './inapp-notification-listing.component.html',
styleUrls: ['./inapp-notification-listing.component.scss']
})
export class InAppNotificationListingComponent extends BaseListingComponent<InAppNotification, InAppNotificationLookup> implements OnInit {
@ViewChild('readColumnTemplate', { static: true }) readColumnTemplate: TemplateRef<any>;
@Input() isPreviewList;
@Output() previewModeChange = new EventEmitter<boolean>();
userSettingsKey = { key: 'InAppNotificationListingUserSettings' };
public notificationInAppTrackingEnum = NotificationInAppTracking;
constructor(
protected router: Router,
protected route: ActivatedRoute,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected queryParamsService: QueryParamsService,
private inappNotificationService: InAppNotificationService,
public authService: AuthService,
private pipeService: PipeService,
) {
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
// Lookup setup
// Default lookup values are defined in the user settings class.
this.lookup = this.initializeLookup();
}
ngOnInit() {
super.ngOnInit();
}
protected initializeLookup(): InAppNotificationLookup {
const lookup = new InAppNotificationLookup();
lookup.metadata = { countAll: true };
lookup.page = { offset: 10, size: 10 };
lookup.isActive = [IsActive.Active];
lookup.order = { items: [nameof<InAppNotification>(x => x.updatedAt)] };
lookup.project = {
fields: [
nameof<InAppNotification>(x => x.id),
nameof<InAppNotification>(x => x.subject),
nameof<InAppNotification>(x => x.trackingState),
nameof<InAppNotification>(x => x.createdAt)
]
};
return lookup;
}
protected setupColumns() {
this.gridColumns.push(...[{
cellTemplate: this.readColumnTemplate,
alwaysShown: true
}, {
prop: nameof<InAppNotification>(x => x.subject),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.SUBJECT'
}, {
prop: nameof<InAppNotification>(x => x.trackingState),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.TRACKING-STATE',
pipe: this.pipeService.getPipe<NotificationInAppTrackingTypePipe>(NotificationInAppTrackingTypePipe)
}, {
prop: nameof<InAppNotification>(x => x.createdAt),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.CREATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
}]);
}
onColumnsChanged(event: ColumnsChangedEvent) {
// Here are defined the projection fields that always requested from the api.
this.lookup.project = {
fields: [
nameof<InAppNotification>(x => x.id),
nameof<InAppNotification>(x => x.trackingState),
...event.properties.filter(x => x).map(x => x.toString())
]
};
this.onPageLoad({ offset: 0 } as PageLoadEvent);
}
protected loadListing() : Observable<QueryResult<InAppNotification>> {
console.log(this.lookup);
return this.inappNotificationService.query(this.lookup);
}
togglePreviewMode(value: boolean) {
this.isPreviewList = value;
this.previewModeChange.emit(value);
}
}

View File

@ -0,0 +1,67 @@
<div class="row inapp-notification-listing">
<div class="col-md-8 offset-md-2">
<div class="row mb-4 mt-3">
<div class="col">
<h4>{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.TITLE' | translate}}</h4>
<app-navigation-breadcrumb />
</div>
</div>
<app-hybrid-listing [rows]="gridRows" [columns]="gridColumns" [visibleColumns]="visibleColumns"
[count]="totalElements" [offset]="currentPageNumber" [limit]="lookup.page.size"
[defaultSort]="lookup.order?.items" [externalSorting]="true" (rowActivated)="onRowActivated($event)"
(pageLoad)="alterPage($event)" (columnSort)="onColumnSort($event)"
(columnsChanged)="onColumnsChanged($event)" [listItemTemplate]="listItemTemplate">
<app-mine-inapp-notification-listing-filters hybrid-listing-filters [(filter)]="lookup"
(filterChange)="filterChanged($event)" />
<app-user-settings-picker [key]="userSettingsKey" [userPreference]="lookup"
(onSettingSelected)="changeSetting($event)" [autoSelectUserSettings]="autoSelectUserSettings"
user-preference-settings />
</app-hybrid-listing>
</div>
</div>
<ng-template #listItemTemplate let-item="item" let-isColumnSelected="isColumnSelected">
<div class="d-flex align-items-center p-3 gap-1-rem">
<div class="row">
<ng-container *ngIf="isColumnSelected('trackingState')">
<a class="buttonLinkClass" [routerLink]="'./' + item?.id" class="col-12"
(click)="$event.stopPropagation()">{{item?.trackingState | nullifyValue}}</a>
<br />
</ng-container>
<ng-container *ngIf="isColumnSelected('createdAt')">
<span class="col-12">
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.CREATED-AT' | translate}}:
<small>
{{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}}
</small>
</span>
<br>
</ng-container>
</div>
</div>
</ng-template>
<ng-template #actions let-row="row" let-item>
<div class="row" (click)="$event.stopPropagation()">
<div class="col-auto">
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
<mat-icon>more_horiz</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
<button mat-menu-item (click)="deleteType(row.id)">
<mat-icon>delete</mat-icon>
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.ACTIONS.DELETE' | translate}}
</button>
</mat-menu>
</div>
</div>
</ng-template>

View File

@ -0,0 +1,61 @@
.inapp-notification-listing {
margin-top: 1.3rem;
margin-left: 1rem;
margin-right: 2rem;
.mat-header-row{
background: #f3f5f8;
}
.mat-card {
margin: 16px 0;
padding: 0px;
}
.mat-row {
cursor: pointer;
min-height: 4.5em;
}
mat-row:hover {
background-color: #eef5f6;
}
.mat-fab-bottom-right {
float: right;
z-index: 5;
}
}
.create-btn {
border-radius: 30px;
background-color: var(--secondary-color);
padding-left: 2em;
padding-right: 2em;
// color: #000;
.button-text{
display: inline-block;
}
}
.dlt-btn {
color: rgba(0, 0, 0, 0.54);
}
.status-chip{
border-radius: 20px;
padding-left: 1em;
padding-right: 1em;
padding-top: 0.2em;
font-size: .8em;
}
.status-chip-finalized{
color: #568b5a;
background: #9dd1a1 0% 0% no-repeat padding-box;
}
.status-chip-draft{
color: #00c4ff;
background: #d3f5ff 0% 0% no-repeat padding-box;
}

View File

@ -0,0 +1,164 @@
import { Component, OnInit, TemplateRef, ViewChild, EventEmitter, Output, Input } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '@app/core/services/auth/auth.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseListingComponent } from '@common/base/base-listing-component';
import { PipeService } from '@common/formatting/pipe.service';
import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { InAppNotification } from '@app/core/model/inapp-notification/inapp-notification.model';
import { InAppNotificationLookup } from '@app/core/query/inapp-notification.lookup';
import { InAppNotificationService } from '@app/core/services/inapp-notification/inapp-notification.service';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { nameof } from 'ts-simple-nameof';
import { NotificationInAppTrackingTypePipe } from '@common/formatting/pipes/notification-inapp-tracking-type.pipe';
import { Observable } from 'rxjs';
import { QueryResult } from '@common/model/query-result';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { Guid } from '@common/types/guid';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-mine-inapp-notification-listing',
templateUrl: './mine-inapp-notification-listing.component.html',
styleUrls: ['./mine-inapp-notification-listing.component.scss']
})
export class MineInAppNotificationListingComponent extends BaseListingComponent<InAppNotification, InAppNotificationLookup> implements OnInit {
publish = false;
userSettingsKey = { key: 'InAppNotificationListingUserSettings' };
propertiesAvailableForOrder: ColumnDefinition[];
@ViewChild('actions', { static: true }) actions?: TemplateRef<any>;
@ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent;
private readonly lookupFields: string[] = [
nameof<InAppNotification>(x => x.id),
nameof<InAppNotification>(x => x.subject),
nameof<InAppNotification>(x => x.trackingState),
nameof<InAppNotification>(x => x.createdAt),
nameof<InAppNotification>(x => x.isActive)
];
rowIdentity = x => x.id;
constructor(
protected router: Router,
protected route: ActivatedRoute,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected queryParamsService: QueryParamsService,
private inAppNotificationService: InAppNotificationService,
public authService: AuthService,
private pipeService: PipeService,
public enumUtils: EnumUtils,
private language: TranslateService,
private dialog: MatDialog
) {
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
// Lookup setup
// Default lookup values are defined in the user settings class.
this.lookup = this.initializeLookup();
}
ngOnInit() {
super.ngOnInit();
}
protected initializeLookup(): InAppNotificationLookup {
const lookup = new InAppNotificationLookup();
lookup.metadata = { countAll: true };
lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE };
lookup.isActive = [IsActive.Active];
lookup.order = { items: [this.toDescSortField(nameof<InAppNotification>(x => x.createdAt))] };
this.updateOrderUiFields(lookup.order);
lookup.project = {
fields: this.lookupFields
};
return lookup;
}
protected setupColumns() {
this.gridColumns.push(...[{
prop: nameof<InAppNotification>(x => x.subject),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.SUBJECT'
},
{
prop: nameof<InAppNotification>(x => x.trackingState),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.TRACKING-STATE',
pipe: this.pipeService.getPipe<NotificationInAppTrackingTypePipe>(NotificationInAppTrackingTypePipe)
},
{
prop: nameof<InAppNotification>(x => x.createdAt),
sortable: true,
languageName: 'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FIELDS.CREATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
alwaysShown: true,
cellTemplate: this.actions,
maxWidth: 120
}
]);
this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable);
}
//
// Listing Component functions
//
onColumnsChanged(event: ColumnsChangedEvent) {
super.onColumnsChanged(event);
this.onColumnsChangedInternal(event.properties.map(x => x.toString()));
}
private onColumnsChangedInternal(columns: string[]) {
// Here are defined the projection fields that always requested from the api.
const fields = new Set(this.lookupFields);
this.gridColumns.map(x => x.prop)
.filter(x => !columns?.includes(x as string))
.forEach(item => {
fields.delete(item as string)
});
this.lookup.project = { fields: [...fields] };
this.onPageLoad({ offset: 0 } as PageLoadEvent);
}
protected loadListing(): Observable<QueryResult<InAppNotification>> {
return this.inAppNotificationService.query(this.lookup);
}
public deleteType(id: Guid) {
if (id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
data: {
isDeleteConfirmation: true,
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.inAppNotificationService.delete(id).pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
});
}
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
this.ngOnInit();
}
}

View File

@ -1,13 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@app/core/auth-guard.service';
import { InAppNotificationListingComponent } from './listing/inapp-notification-listing.component';
import { MineInAppNotificationListingComponent } from './listing/mine-inapp-notification-listing.component';
import { InAppNotificationEditorComponent } from './editor/inapp-notification-editor.component';
const routes: Routes = [
{
path: '',
component: InAppNotificationListingComponent,
component: MineInAppNotificationListingComponent,
data: {
},
canActivate: [AuthGuard]
@ -32,4 +32,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class InAppNotificationRoutingModule { }
export class MineInAppNotificationRoutingModule { }

View File

@ -4,15 +4,12 @@ import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/co
import { TextFilterModule } from '@common/modules/text-filter/text-filter.module';
import { UserSettingsModule } from '@common/modules/user-settings/user-settings.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { InAppNotificationRoutingModule } from './inapp-notification-routing.module';
import { InAppNotificationListingComponent } from './listing/inapp-notification-listing.component';
import { MineInAppNotificationRoutingModule } from './mine-inapp-notification-routing.module';
import { MineInAppNotificationListingComponent } from './listing/mine-inapp-notification-listing.component';
import { HybridListingModule } from '@common/modules/hybrid-listing/hybrid-listing.module';
import { InAppNotificationEditorComponent } from './editor/inapp-notification-editor.component';
import { InAppNotificationListingFiltersComponent } from './listing/filters/inapp-notification-listing-filters.component';
import { InAppNotificationListingDialogComponent } from './listing-dialog/inapp-notification-listing-dialog.component';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { NotificationInAppTrackingTypePipe } from '@common/formatting/pipes/notification-inapp-tracking-type.pipe';
import { DatePipe } from '@angular/common';
import { MineInAppNotificationListingFiltersComponent } from './listing/filters/mine-inapp-notification-listing-filters.component';
import { MineInAppNotificationListingDialogComponent } from './listing-dialog/mine-inapp-notification-listing-dialog.component';
import { CommonFormattingModule } from '@common/formatting/common-formatting.module';
@NgModule({
@ -22,21 +19,18 @@ import { CommonFormattingModule } from '@common/formatting/common-formatting.mod
ConfirmationDialogModule,
HybridListingModule,
TextFilterModule,
InAppNotificationRoutingModule,
MineInAppNotificationRoutingModule,
UserSettingsModule,
CommonFormattingModule
],
declarations: [
InAppNotificationListingComponent,
MineInAppNotificationListingComponent,
InAppNotificationEditorComponent,
InAppNotificationListingFiltersComponent,
InAppNotificationListingDialogComponent
MineInAppNotificationListingFiltersComponent,
MineInAppNotificationListingDialogComponent
],
// entryComponents: [
// InAppNotificationListingDialogComponent
// ],
exports: [
InAppNotificationListingComponent,
MineInAppNotificationListingComponent,
]
})
export class InAppNotificationModule { }
export class MineInAppNotificationModule { }

View File

@ -32,7 +32,7 @@
<app-language (languageChange)="getLanguage($event)"></app-language>
</mat-menu>
</div>
<div class="col-auto" *ngIf="isAuthenticated() && authentication.hasPermission(authentication.permissionEnum.ViewInAppNotificationPage)">
<div class="col-auto" *ngIf="isAuthenticated() && authentication.hasPermission(authentication.permissionEnum.ViewMineInAppNotificationPage)">
<button class="col-auto" mat-icon-button matTooltip="{{'NAV-BAR.INAPP-NOTIFICATIONS' | translate}}" (click)="toggleInAppNotifications()">
<mat-icon [matBadge]="inAppNotificationCount" [matBadgeHidden]="inAppNotificationCount <= 0" matBadgeColor="warn">mail</mat-icon>
</button>

View File

@ -16,7 +16,7 @@ import { StartNewDmpDialogComponent } from '../dmp/new/start-new-dmp-dialogue/st
import { FaqDialogComponent } from '../faq/dialog/faq-dialog.component';
import { UserDialogComponent } from './user-dialog/user-dialog.component';
import { DATASETS_ROUTES, DMP_ROUTES, GENERAL_ROUTES } from '../sidebar/sidebar.component';
import { InAppNotificationListingDialogComponent } from '../inapp-notification/listing-dialog/inapp-notification-listing-dialog.component';
import { MineInAppNotificationListingDialogComponent } from '../inapp-notification/listing-dialog/mine-inapp-notification-listing-dialog.component';
import { InAppNotificationService } from '@app/core/services/inapp-notification/inapp-notification.service';
import { timer } from 'rxjs';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
@ -37,7 +37,7 @@ export class NavbarComponent extends BaseComponent implements OnInit {
currentRoute: string;
selectedLanguage: string;
private user: User;
inAppNotificationDialog: MatDialogRef<InAppNotificationListingDialogComponent> = null;
inAppNotificationDialog: MatDialogRef<MineInAppNotificationListingDialogComponent> = null;
inAppNotificationCount = 0;
@Output() sidebarToggled: EventEmitter<any> = new EventEmitter();
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
@ -303,7 +303,7 @@ export class NavbarComponent extends BaseComponent implements OnInit {
this.inAppNotificationDialog.close();
} else {
this.countUnreadInappNotifications();
this.inAppNotificationDialog = this.dialog.open(InAppNotificationListingDialogComponent, {
this.inAppNotificationDialog = this.dialog.open(MineInAppNotificationListingDialogComponent, {
position: {
top: '64px', right: '0px'
}

View File

@ -254,7 +254,8 @@
"LANGUAGES": "Languages",
"NEW-LANGUAGE": "New",
"EDIT-LANGUAGE": "Edit",
"NOTIFICATION-TEMPLATES": "Notification Templates"
"NOTIFICATION-TEMPLATES": "Notification Templates",
"INAPP-NOTIFICATIONS":"Notifications"
},
"COOKIE": {
"MESSAGE": "This website uses cookies to enhance the user experience.",
@ -1331,17 +1332,16 @@
},
"NOTIFICATION-SERVICE": {
"INAPP-NOTIFICATION-LISTING": {
"TITLE": "Notifications",
"FIELDS": {
"SUBJECT": "Subject",
"TRACKING-STATE": "Tracking State",
"CREATED-AT": "Created",
"IS-ACTIVE": "Is Active"
"TRACKING-STATE": "State",
"CREATED-AT": "Created"
},
"FILTER": {
"MORE-FILTERS": "More filters",
"TRACKING-STATE":"Tracking State",
"TITLE": "Filters",
"TRACKING-STATE":"State",
"IS-ACTIVE": "Is Active",
"CHANNEL": "Channel",
"CANCEL": "Cancel",
"APPLY-FILTERS": "Apply filters"
},
@ -1362,7 +1362,6 @@
},
"ACTIONS": {
"READ": "Read",
"CANCEL": "Cancel",
"DELETE": "Delete"
}
@ -2316,8 +2315,8 @@
"PUBLIC-CONTACT-SUPPORT": "Public Contact Support"
},
"NOTIFICATION-INAPP-TRACKING": {
"STORED": "Stored",
"DELIVERED": "Delivered"
"STORED": "Unread",
"DELIVERED": "Read"
}
},
"ADDRESEARCHERS-EDITOR": {