custom notifications service

This commit is contained in:
2023-01-08 16:57:21 +01:00
parent c10e925445
commit 80f09b6f18
14 changed files with 187 additions and 163 deletions

View File

@@ -14,6 +14,7 @@ import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MomentDateAdapter } from '@angular/material-moment-adapter'; import { MAT_MOMENT_DATE_ADAPTER_OPTIONS, MomentDateAdapter } from '@angular/material-moment-adapter';
import { registerLocaleData } from '@angular/common'; import { registerLocaleData } from '@angular/common';
import localePl from '@angular/common/locales/pl'; import localePl from '@angular/common/locales/pl';
import { NotificationsComponent } from './components/notifications/notifications.component';
registerLocaleData(localePl, 'pl-PL'); registerLocaleData(localePl, 'pl-PL');
@@ -21,7 +22,8 @@ registerLocaleData(localePl, 'pl-PL');
declarations: [ declarations: [
AppComponent, AppComponent,
MainViewComponent, MainViewComponent,
LoginPageComponent LoginPageComponent,
NotificationsComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@@ -1,9 +1,9 @@
import { Component, NgZone, OnInit } from '@angular/core'; import { Component, NgZone, OnInit } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import jwt_decode from "jwt-decode"; import jwt_decode from "jwt-decode";
import { AuthService } from 'src/app/auth/auth.service'; import { AuthService } from 'src/app/auth/auth.service';
import { User } from 'src/app/models/user.model'; import { User } from 'src/app/models/user.model';
import { NotificationsService } from 'src/app/services/notifications.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
@@ -17,14 +17,12 @@ export class LoginPageComponent implements OnInit {
private router$: Router, private router$: Router,
private auth$: AuthService, private auth$: AuthService,
private ngZone$: NgZone, private ngZone$: NgZone,
private snackBar$: MatSnackBar private notifications$: NotificationsService
) { } ) { }
loading = false; loading = false;
ngOnInit(): void { ngOnInit(): void {
this.snackBar$.open("", "", { duration: 1 });
console.log('Envs', environment);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
window.onGoogleLibraryLoad = () => { window.onGoogleLibraryLoad = () => {
@@ -68,11 +66,18 @@ export class LoginPageComponent implements OnInit {
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
console.error('handleCredentialResponse', e);
this.loading = false; this.loading = false;
if (e.status === 401) { if (e.status === 401) {
this.snackBar$.open("Użytkownik nie istnieje w bazie danych aplikacji DiunaBI.", "", { duration: 3000 }); this.notifications$.add({
text: "Użytkownik nie istnieje w bazie danych aplikacji DiunaBI.",
duration: 2500
});
} else { } else {
this.snackBar$.open("Błąd połączenia z serwerem. Skontaktuj się z administratorem.", "", { duration: 3000 }); this.notifications$.add({
text: "Błąd połączenia z serwerem. Skontaktuj się z administratorem.",
duration: 2500
});
} }
} finally { } finally {
this.loading = false; this.loading = false;

View File

@@ -0,0 +1,8 @@
<mat-card *ngFor="let msg of notifications$.messages">
<mat-card-content>
<span class="text">{{msg.text}}</span>
<span class="btn" *ngIf="msg.btn">
<a class="action-button" (click)="notifications$.doAction(msg)">{{msg.btn}}</a>
</span>
</mat-card-content>
</mat-card>

View File

@@ -0,0 +1,17 @@
mat-card {
margin-bottom: 3px;
background-color: rgba(255, 145, 0, 0.4);
}
.action-button {
cursor: pointer;
}
.text {
display: inline-block;
width: 70%;
}
.btn {
display: inline-block;
width: 30%;
text-align: right;
color: red;
}

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationsComponent } from './notifications.component';
describe('NotificationsComponent', () => {
let component: NotificationsComponent;
let fixture: ComponentFixture<NotificationsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NotificationsComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(NotificationsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { NotificationsService } from 'src/app/services/notifications.service';
@Component({
selector: 'app-notifications',
templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.scss'],
})
export class NotificationsComponent {
constructor(
public notifications$: NotificationsService
) {}
}

View File

@@ -120,106 +120,9 @@ textarea[disabled] {
color: rgb(205, 206, 177); color: rgb(205, 206, 177);
font-size: larger; font-size: larger;
} }
.errorMsg {
font-size: small;
margin-top: 10px;
}
/* links */ /* links */
a:link, a:link,
a:visited { a:visited {
color: black; color: black;
} }
.stock-state-error {
color: red;
font-weight: bold;
}
.listContainer {
height: 100%;
width: 100%;
overflow: scroll;
}
.listItem {
height: 40px;
font-size: medium;
}
.listItem:hover {
width: 100%;
cursor: pointer;
background: rgba(130, 130, 130, 0.2);
}
/* snack bars */
.snack-error {
background: #f44336;
}
.input-right {
display: block;
direction: rtl;
}
.background-red {
background-color: rgba(255, 0, 0, 0.6);
}
.background-orange {
background-color: rgba(255, 145, 0, 0.6);
}
.background-green {
background-color: rgba(0, 255, 0, 0.6);
}
.background-grey {
background-color: rgba(100, 100, 100, 0.3);
}
.mat-mdc-dialog-container {
padding: 0px !important;
}
.tab-loader {
width: 25px;
margin-right: 10px;
}
.cell-border {
border-left: 1px solid #e0e0e0;
padding-left: 4px;
}
.cell-background {
background-color: #eeeeee;
}
.breadcrumbs {
width: 100%;
text-align: right;
font-size: small;
color: gray;
margin-top: 0px;
padding-top: 0px;
}
.breadcrumbs a {
color: gray;
text-decoration: none;
}
::ng-deep snack-bar-container.custom-snackbar-error {
background: #ff3452;
}
::ng-deep snack-bar-container.custom-snackbar-success {
background: #9ad284;
}
::ng-deep .mat-mdc-simple-snack-bar {
color: #000;
}
::ng-deep .mat-mdc-snack-bar-action {
color: #000;
}

View File

@@ -9,6 +9,7 @@ import { environment } from 'src/environments/environment';
import { AuthService } from '../auth/auth.service'; import { AuthService } from '../auth/auth.service';
import { DataService } from '../services/data.service'; import { DataService } from '../services/data.service';
import { DeviceService } from '../services/device.service'; import { DeviceService } from '../services/device.service';
import { NotificationsService } from '../services/notifications.service';
@Component({ @Component({
selector: 'app-main-view', selector: 'app-main-view',
@@ -30,7 +31,7 @@ export class MainViewComponent {
private router$: Router, private router$: Router,
private swUpdate$: SwUpdate, private swUpdate$: SwUpdate,
private ngZone$: NgZone, private ngZone$: NgZone,
public auth$: AuthService public auth$: AuthService,
) { ) {
this.swUpdate$.versionUpdates.subscribe(() => { this.swUpdate$.versionUpdates.subscribe(() => {
this.reloadApp(); this.reloadApp();

View File

@@ -1,72 +1,72 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import { MatAutocompleteModule } from '@angular/material/autocomplete'; //import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle'; //import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox'; //import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatChipsModule } from '@angular/material/chips'; //import { MatChipsModule } from '@angular/material/chips';
import { MatStepperModule } from '@angular/material/stepper'; //import { MatStepperModule } from '@angular/material/stepper';
import { MatDatepickerModule } from '@angular/material/datepicker'; //import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog'; //import { MatDialogModule } from '@angular/material/dialog';
import { MatExpansionModule } from '@angular/material/expansion'; //import { MatExpansionModule } from '@angular/material/expansion';
import { MatGridListModule } from '@angular/material/grid-list'; import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list'; import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator'; import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar'; //import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; //import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatRadioModule } from '@angular/material/radio'; //import { MatRadioModule } from '@angular/material/radio';
import { MatRippleModule } from '@angular/material/core'; //import { MatRippleModule } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSliderModule } from '@angular/material/slider'; //import { MatSliderModule } from '@angular/material/slider';
import { MatSlideToggleModule } from '@angular/material/slide-toggle'; //import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatTabsModule } from '@angular/material/tabs'; //import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip'; //import { MatTooltipModule } from '@angular/material/tooltip';
import {CdkTableModule} from '@angular/cdk/table'; //import {CdkTableModule} from '@angular/cdk/table';
import {MatBadgeModule} from '@angular/material/badge'; //import {MatBadgeModule} from '@angular/material/badge';
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
@NgModule({ @NgModule({
exports: [ exports: [
CdkTableModule, // CdkTableModule,
MatAutocompleteModule, // MatAutocompleteModule,
MatButtonModule, MatButtonModule,
MatButtonToggleModule, // MatButtonToggleModule,
MatCardModule, MatCardModule,
MatCheckboxModule, // MatCheckboxModule,
MatChipsModule, // MatChipsModule,
MatStepperModule, // MatStepperModule,
MatDatepickerModule, // MatDatepickerModule,
MatDialogModule, // MatDialogModule,
MatExpansionModule, // MatExpansionModule,
MatGridListModule, MatGridListModule,
MatIconModule, MatIconModule,
MatInputModule, MatInputModule,
MatListModule, MatListModule,
MatMenuModule, MatMenuModule,
MatPaginatorModule, MatPaginatorModule,
MatProgressBarModule, // MatProgressBarModule,
MatProgressSpinnerModule, // MatProgressSpinnerModule,
MatRadioModule, // MatRadioModule,
MatRippleModule, // MatRippleModule,
MatSelectModule, MatSelectModule,
MatSidenavModule, // MatSidenavModule,
MatSliderModule, // MatSliderModule,
MatSlideToggleModule, // MatSlideToggleModule,
MatSnackBarModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule,
MatTabsModule, // MatTabsModule,
MatToolbarModule, MatToolbarModule,
MatTooltipModule, // MatTooltipModule,
MatSidenavModule, MatSidenavModule,
MatBadgeModule // MatBadgeModule,
MatBottomSheetModule
], ],
providers: [] providers: []
}) })

View File

@@ -2,14 +2,13 @@ import { DatePipe } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms'; import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms';
import { MatPaginator } from '@angular/material/paginator';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatSort, MatSortable } from '@angular/material/sort'; import { MatSort, MatSortable } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router, ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { AuthService } from 'src/app/auth/auth.service'; import { AuthService } from 'src/app/auth/auth.service';
import { Layer } from 'src/app/models/layer.model'; import { Layer } from 'src/app/models/layer.model';
import { Record } from 'src/app/models/record.model'; import { Record } from 'src/app/models/record.model';
import { NotificationsService } from 'src/app/services/notifications.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
@@ -29,12 +28,11 @@ export class LayerDetailComponent implements OnInit {
constructor( constructor(
private fb$: UntypedFormBuilder, private fb$: UntypedFormBuilder,
private router$: Router,
private http$: HttpClient, private http$: HttpClient,
private route$: ActivatedRoute, private route$: ActivatedRoute,
private auth$: AuthService, private auth$: AuthService,
private snackBar: MatSnackBar, private datePipe: DatePipe,
private datePipe: DatePipe private notifications$: NotificationsService
) { } ) { }
async ngOnInit() { async ngOnInit() {
@@ -55,9 +53,13 @@ export class LayerDetailComponent implements OnInit {
} }
async export() { async export() {
if (await Layer.exportToGoogleSheet(this.document.id || "", this.http$)) { if (await Layer.exportToGoogleSheet(this.document.id || "", this.http$)) {
this.snackBar.open("Plik został zapisany na dysku Google", "OK"); this.notifications$.add({
text: "Plik został zapisany na dysku Google.",
});
} else { } else {
this.snackBar.open("Zapis się nie udał.", "OK"); this.notifications$.add({
text: "Zapis się nie udał.",
});
} }
} }
} }

View File

@@ -1,7 +1,6 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';

View File

@@ -0,0 +1,59 @@
import { Injectable } from '@angular/core';
import { MatBottomSheet, MatBottomSheetRef } from '@angular/material/bottom-sheet';
import { NotificationsComponent } from '../components/notifications/notifications.component';
import { v4 as uuidv4 } from 'uuid';
import moment, { Moment } from 'moment';
@Injectable({
providedIn: 'root'
})
export class NotificationsService {
public messages: Message[] = [];
private handler?: MatBottomSheetRef;
constructor(
private bottomSheet$: MatBottomSheet
) { }
public add(message: Message) {
message.id = uuidv4();
message.createdAt = moment();
this.messages.push(message);
this.sortMessages();
if (this.messages.length === 1) {
this.handler = this.bottomSheet$.open(NotificationsComponent, {
hasBackdrop: false,
disableClose: true
});
}
setTimeout(() => {
this.remove(message);
}, message.duration ? message.duration : 5000);
}
private remove(message: Message) {
this.messages = this.messages.filter(x => x.id!==message.id);
this.sortMessages();
if (this.messages.length === 0 && this.handler) {
this.handler.dismiss();
}
}
private sortMessages() {
this.messages = this.messages.sort((a, b) => {
return a.createdAt && a.createdAt.isAfter(b.createdAt) ? 1 : -1;
})
}
doAction(message: Message) {
if (message.action) { message.action(); }
this.remove(message);
}
}
interface Message {
id?: string;
text: string;
duration?: number;
btn?: string;
// eslint-disable-next-line @typescript-eslint/ban-types
action?: Function;
createdAt?: Moment
}

View File

@@ -44,14 +44,6 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
z-index: 100; z-index: 100;
} }
.snackbar-success {
background: #BEFF33;
}
.snackbar-error {
background: #e7334b;
}
:root { :root {
--avatar-size: 30px; --avatar-size: 30px;
} }

View File

@@ -8,7 +8,7 @@
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"SQLDatabase": "Server=tcp:127.0.0.1,1433;Initial Catalog=diuna;Persist Security Info=False;User ID=SA;Password=v](8Lc|RfG;MultipleActiveResultSets=False;Encrypt=False;TrustServerCertificate=False;Connection Timeout=30;" "SQLDatabase": "Server=tcp:127.0.0.1,1433;Initial Catalog=diunabi-morska;Persist Security Info=False;User ID=SA;Password=v](8Lc|RfG;MultipleActiveResultSets=False;Encrypt=False;TrustServerCertificate=False;Connection Timeout=30;"
}, },
"GoogleClientId": "107631825312-bkfe438ehr9k9ecb2h76g802tj6advma.apps.googleusercontent.com", "GoogleClientId": "107631825312-bkfe438ehr9k9ecb2h76g802tj6advma.apps.googleusercontent.com",
"Secret": "8393AF8EAEF8478CB738D44858690F9C7E2D19F65896DD9FBAA3EB2A6F493E80", "Secret": "8393AF8EAEF8478CB738D44858690F9C7E2D19F65896DD9FBAA3EB2A6F493E80",