custom notifications service
This commit is contained in:
@@ -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 { registerLocaleData } from '@angular/common';
|
||||
import localePl from '@angular/common/locales/pl';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
|
||||
registerLocaleData(localePl, 'pl-PL');
|
||||
|
||||
@@ -21,7 +22,8 @@ registerLocaleData(localePl, 'pl-PL');
|
||||
declarations: [
|
||||
AppComponent,
|
||||
MainViewComponent,
|
||||
LoginPageComponent
|
||||
LoginPageComponent,
|
||||
NotificationsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Component, NgZone, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Router } from '@angular/router';
|
||||
import jwt_decode from "jwt-decode";
|
||||
import { AuthService } from 'src/app/auth/auth.service';
|
||||
import { User } from 'src/app/models/user.model';
|
||||
import { NotificationsService } from 'src/app/services/notifications.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Component({
|
||||
@@ -17,14 +17,12 @@ export class LoginPageComponent implements OnInit {
|
||||
private router$: Router,
|
||||
private auth$: AuthService,
|
||||
private ngZone$: NgZone,
|
||||
private snackBar$: MatSnackBar
|
||||
private notifications$: NotificationsService
|
||||
) { }
|
||||
|
||||
loading = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.snackBar$.open("", "", { duration: 1 });
|
||||
console.log('Envs', environment);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.onGoogleLibraryLoad = () => {
|
||||
@@ -68,11 +66,18 @@ export class LoginPageComponent implements OnInit {
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
console.error('handleCredentialResponse', e);
|
||||
this.loading = false;
|
||||
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 {
|
||||
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 {
|
||||
this.loading = false;
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
@@ -120,106 +120,9 @@ textarea[disabled] {
|
||||
color: rgb(205, 206, 177);
|
||||
font-size: larger;
|
||||
}
|
||||
.errorMsg {
|
||||
font-size: small;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* links */
|
||||
a:link,
|
||||
a:visited {
|
||||
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;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { environment } from 'src/environments/environment';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { DataService } from '../services/data.service';
|
||||
import { DeviceService } from '../services/device.service';
|
||||
import { NotificationsService } from '../services/notifications.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-main-view',
|
||||
@@ -30,7 +31,7 @@ export class MainViewComponent {
|
||||
private router$: Router,
|
||||
private swUpdate$: SwUpdate,
|
||||
private ngZone$: NgZone,
|
||||
public auth$: AuthService
|
||||
public auth$: AuthService,
|
||||
) {
|
||||
this.swUpdate$.versionUpdates.subscribe(() => {
|
||||
this.reloadApp();
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
//import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
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 { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
//import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
//import { MatChipsModule } from '@angular/material/chips';
|
||||
//import { MatStepperModule } from '@angular/material/stepper';
|
||||
//import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
//import { MatDialogModule } from '@angular/material/dialog';
|
||||
//import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
//import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
//import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
//import { MatRadioModule } from '@angular/material/radio';
|
||||
//import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
//import { MatSliderModule } from '@angular/material/slider';
|
||||
//import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
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 { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import {CdkTableModule} from '@angular/cdk/table';
|
||||
import {MatBadgeModule} from '@angular/material/badge';
|
||||
//import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
//import {CdkTableModule} from '@angular/cdk/table';
|
||||
//import {MatBadgeModule} from '@angular/material/badge';
|
||||
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
|
||||
|
||||
@NgModule({
|
||||
exports: [
|
||||
CdkTableModule,
|
||||
MatAutocompleteModule,
|
||||
// CdkTableModule,
|
||||
// MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
// MatButtonToggleModule,
|
||||
MatCardModule,
|
||||
MatCheckboxModule,
|
||||
MatChipsModule,
|
||||
MatStepperModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatExpansionModule,
|
||||
// MatCheckboxModule,
|
||||
// MatChipsModule,
|
||||
// MatStepperModule,
|
||||
// MatDatepickerModule,
|
||||
// MatDialogModule,
|
||||
// MatExpansionModule,
|
||||
MatGridListModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatRadioModule,
|
||||
MatRippleModule,
|
||||
// MatProgressBarModule,
|
||||
// MatProgressSpinnerModule,
|
||||
// MatRadioModule,
|
||||
// MatRippleModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatSliderModule,
|
||||
MatSlideToggleModule,
|
||||
MatSnackBarModule,
|
||||
// MatSidenavModule,
|
||||
// MatSliderModule,
|
||||
// MatSlideToggleModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
// MatTabsModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule,
|
||||
// MatTooltipModule,
|
||||
MatSidenavModule,
|
||||
MatBadgeModule
|
||||
// MatBadgeModule,
|
||||
MatBottomSheetModule
|
||||
],
|
||||
providers: []
|
||||
})
|
||||
|
||||
@@ -2,14 +2,13 @@ import { DatePipe } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
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 { 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 { Layer } from 'src/app/models/layer.model';
|
||||
import { Record } from 'src/app/models/record.model';
|
||||
import { NotificationsService } from 'src/app/services/notifications.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Component({
|
||||
@@ -29,12 +28,11 @@ export class LayerDetailComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private fb$: UntypedFormBuilder,
|
||||
private router$: Router,
|
||||
private http$: HttpClient,
|
||||
private route$: ActivatedRoute,
|
||||
private auth$: AuthService,
|
||||
private snackBar: MatSnackBar,
|
||||
private datePipe: DatePipe
|
||||
private datePipe: DatePipe,
|
||||
private notifications$: NotificationsService
|
||||
) { }
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -55,9 +53,13 @@ export class LayerDetailComponent implements OnInit {
|
||||
}
|
||||
async export() {
|
||||
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 {
|
||||
this.snackBar.open("Zapis się nie udał.", "OK");
|
||||
this.notifications$.add({
|
||||
text: "Zapis się nie udał.",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
|
||||
59
Frontend/src/app/services/notifications.service.ts
Normal file
59
Frontend/src/app/services/notifications.service.ts
Normal 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
|
||||
}
|
||||
@@ -44,14 +44,6 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.snackbar-success {
|
||||
background: #BEFF33;
|
||||
}
|
||||
|
||||
.snackbar-error {
|
||||
background: #e7334b;
|
||||
}
|
||||
|
||||
:root {
|
||||
--avatar-size: 30px;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"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",
|
||||
"Secret": "8393AF8EAEF8478CB738D44858690F9C7E2D19F65896DD9FBAA3EB2A6F493E80",
|
||||
|
||||
Reference in New Issue
Block a user