Index: Farmatiko/ClientApp/src/app/admin/admin.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/admin/admin.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/admin/admin.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -4,5 +4,5 @@
 import { Router } from '@angular/router';
 import { IPharmacyHead, IPharmacyHeadRequest, IPharmacy } from '../shared/interfaces';
-import { DataService } from '../shared/data.service';
+import { DataService } from '../shared/services/data.service';
 import { EditPharmacyHeadDialogComponent } from '../dialogs/edit-pharmacy-head-dialog/edit-pharmacy-head-dialog.component';
 import { PharmacyDialogComponent } from '../dialogs/pharmacy-dialog/pharmacy-dialog.component';
Index: Farmatiko/ClientApp/src/app/app.module.ts
===================================================================
--- Farmatiko/ClientApp/src/app/app.module.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/app.module.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -7,6 +7,8 @@
 import { ReactiveFormsModule } from '@angular/forms';
 
-import { DataService } from './shared/data.service';
+import { CoreModule } from './shared/core.module';
 
+import { AuthGuard } from './shared/guards/auth.guard';
+import { DataService } from './shared/services/data.service';
 import { AppComponent } from './app.component';
 import { NavMenuComponent } from './nav-menu/nav-menu.component';
@@ -25,5 +27,4 @@
 import { EditPharmacyHeadDialogComponent } from './dialogs/edit-pharmacy-head-dialog/edit-pharmacy-head-dialog.component';
 import { PharmacyHeadDialogComponent } from './nav-menu/dialogs/pharmacy-head-dialog/pharmacy-head-dialog.component';
-import { AuthGuard } from './shared/auth.guard';
 
 @NgModule({
@@ -53,5 +54,5 @@
       { path: 'mapa', component: CounterComponent },
       { path: 'koronavirus', component: KoronaComponent },
-      { path: 'admin', component: AdminComponent },
+      { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
       { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
       { path: 'login', component: LoginComponent }
@@ -59,5 +60,6 @@
     BrowserAnimationsModule,
     MaterialModule,
-    ReactiveFormsModule
+    ReactiveFormsModule,
+    CoreModule
   ],
   providers: [
Index: Farmatiko/ClientApp/src/app/counter/counter.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/counter/counter.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/counter/counter.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -1,5 +1,5 @@
 import { Component, OnInit } from '@angular/core';
 import { IHealthFacilities, IHealthcareWorkers } from '../shared/interfaces';
-import { DataService } from '../shared/data.service';
+import { DataService } from '../shared/services/data.service';
 import { MatDialog } from '@angular/material/dialog';
 import { FacilityDialogComponent } from '../dialogs/facility-dialog/facility-dialog.component';
Index: Farmatiko/ClientApp/src/app/dashboard/dashboard.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/dashboard/dashboard.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/dashboard/dashboard.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -3,10 +3,10 @@
 import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material/snack-bar';
 import { IPharmacy, IMedicine, IPharmacyHead, IPharmacyHeadRequest } from '../shared/interfaces';
-import { DataService } from '../shared/data.service';
+import { DataService } from '../shared/services/data.service';
 import { PharmacyDialogComponent } from '../dialogs/pharmacy-dialog/pharmacy-dialog.component';
 import { EditPharmacyDialogComponent } from '../dialogs/edit-pharmacy-dialog/edit-pharmacy-dialog.component';
 import { MedicineDialogComponent } from '../dialogs/medicine-dialog/medicine-dialog.component';
 import { ActivatedRoute, Router } from '@angular/router';
-import { AuthService } from '../shared/auth.service';
+import { AuthService } from '../shared/services/auth.service';
 
 @Component({
@@ -27,5 +27,7 @@
 
   ngOnInit(): void {
-    this.head = this.authService.headValue;
+    this.authService.getUser().subscribe((data : IPharmacyHead) => {
+        this.head = data;
+    });
     this.dataService.getPharmacies()
         .subscribe((pharmacy: IPharmacy[]) => {
Index: Farmatiko/ClientApp/src/app/home/home.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/home/home.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/home/home.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -1,5 +1,5 @@
 import { Component, OnInit } from '@angular/core';
 import { IMedicine, IPharmacy } from '../shared/interfaces';
-import { DataService } from '../shared/data.service';
+import { DataService } from '../shared/services/data.service';
 import { MatDialog } from '@angular/material/dialog';
 import { MedicineDialogComponent } from '../dialogs/medicine-dialog/medicine-dialog.component';
Index: Farmatiko/ClientApp/src/app/korona/korona.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/korona/korona.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/korona/korona.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -1,4 +1,4 @@
 import { Component, OnInit } from '@angular/core';
-import { DataService } from '../shared/data.service';
+import { DataService } from '../shared/services/data.service';
 import { IPandemic } from '../shared/interfaces';
 
Index: Farmatiko/ClientApp/src/app/login/login.component.html
===================================================================
--- Farmatiko/ClientApp/src/app/login/login.component.html	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/login/login.component.html	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -6,12 +6,16 @@
   <div class="example-container">
     <mat-form-field>
-      <input matInput placeholder="Email" [(ngModel)]="this.email" formControlName="email">
+      <input matInput placeholder="Email" [(ngModel)]="this.username" formControlName="email">
     </mat-form-field>
 
     <mat-form-field>
-      <input matInput type="password" placeholder="Password" [(ngModel)]="this.passwd" formControlName="password">    
+      <input matInput type="password" placeholder="Password" [(ngModel)]="this.password" formControlName="password">    
     </mat-form-field>
 
     <button [disabled]="!loginForm.valid" mat-raised-button color="primary" (click)="loginPharmacyHead()" mat-button>Најави се</button>
+
+    <div class="checkbox mb-3 text-danger" *ngIf="loginError">
+      Login failed. Please try again.
+    </div>
   </div>
 </form>
Index: Farmatiko/ClientApp/src/app/login/login.component.ts
===================================================================
--- Farmatiko/ClientApp/src/app/login/login.component.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/login/login.component.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -1,7 +1,9 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
 import { Router, ActivatedRoute } from '@angular/router';
+import { Subscription } from 'rxjs';
 import { first } from 'rxjs/operators';
-import { AuthService } from '../shared/auth.service';
+import { AuthService } from '../shared/services/auth.service';
+import { finalize } from 'rxjs/operators';
 
 @Component({
@@ -10,30 +12,52 @@
   styleUrls: ['./login.component.css']
 })
-export class LoginComponent implements OnInit {
+export class LoginComponent implements OnInit, OnDestroy {
+  busy = false;
   loginForm: FormGroup;
-  email: string;
-  passwd: string;
-  errorMessage: string;
+  username = '';
+  password = '';
+  loginError = false;
+  private subscription: Subscription;
 
   constructor(private authService: AuthService,private router: Router, private route: ActivatedRoute) {
     this.loginForm = new FormGroup({
-      email: new FormControl('', [Validators.required, Validators.email]),
+      username: new FormControl('', [Validators.required, Validators.email]),
       password: new FormControl('', [Validators.required])
     });
   }
+  ngOnDestroy(): void {
+    this.subscription?.unsubscribe();
+  }
 
   ngOnInit(): void {
+    this.subscription = this.authService.user$.subscribe((x) => {
+      if (this.route.snapshot.url[0].path === 'login') {
+        const accessToken = localStorage.getItem('access_token');
+        const refreshToken = localStorage.getItem('refresh_token');
+        if (x && accessToken && refreshToken) {
+          const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '';
+          this.router.navigate([returnUrl]);
+        }
+      }
+    });
   }
 
   loginPharmacyHead() {
-    this.authService.login(this.email,this.passwd)
-        .pipe(first())
-        .subscribe((data) => {
-            if(data) {
-              this.router.navigate(['/dashboard']);
-            }},
-            (err: any) => {
-                this.errorMessage = err.toString();
-            });
+    if (!this.username || !this.password) {
+      return;
+    }
+    this.busy = true;
+    const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '';
+    this.authService
+      .login(this.username, this.password)
+      .pipe(finalize(() => (this.busy = false)))
+      .subscribe(
+        () => {
+          this.router.navigate(['/dashboard']);
+        },
+        () => {
+          this.loginError = true;
+        }
+      );
     this.loginForm.reset();
   }
Index: rmatiko/ClientApp/src/app/shared/auth.guard.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/auth.guard.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ 	(revision )
@@ -1,24 +1,0 @@
-import { Injectable } from '@angular/core';
-import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
-import { Observable } from 'rxjs';
-
-import { AuthService } from './auth.service';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class AuthGuard implements CanActivate {
-  constructor(private router: Router, private authService: AuthService) {
-
-  }
-
-  canActivate(next: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
-    const head = this.authService.headValue;
-    if(head) {
-      return true;
-    }
-    this.router.navigate(['/login']);
-    return false;
-  }
-  
-}
Index: rmatiko/ClientApp/src/app/shared/auth.service.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/auth.service.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ 	(revision )
@@ -1,54 +1,0 @@
-import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
-import { HttpClient, HttpErrorResponse } from '@angular/common/http';
-import { BehaviorSubject, Observable } from 'rxjs';
-import { catchError, map } from 'rxjs/operators';
-import { IPharmacyHead } from './interfaces';
-import { DataService } from './data.service';
-
-
-@Injectable({
-  providedIn: 'root'
-})
-export class AuthService {
-  private headSubject: BehaviorSubject<IPharmacyHead>;
-  public head: Observable<IPharmacyHead>;
-  basePharmacyHead: string = '/api/pharmacyhead';
-
-  constructor(private router: Router, private http: HttpClient, private dataService: DataService) {
-    this.headSubject = new BehaviorSubject<IPharmacyHead>(JSON.parse(localStorage.getItem('head')));
-    this.head = this.headSubject.asObservable();
-  }
-
-  public get headValue(): IPharmacyHead {
-    return this.headSubject.value;
-  }
-
-  login(email: string, passwd: string) : Observable<any> {
-    let postData = {email : email ,password : passwd};
-    return this.http.post<any>(this.basePharmacyHead + '/login', postData)
-                .pipe(
-                    map((data) => {
-                        localStorage.setItem('head', JSON.stringify(data));
-                        this.headSubject.next(data);
-                        return data;
-                    }),
-                    catchError(this.handleError)
-                );
-  }
-
-  logout() {
-      localStorage.removeItem('user');
-      this.headSubject.next(null);
-      this.router.navigate(['/login']);
-  }
-
-  private handleError(error: HttpErrorResponse) {
-    console.error('server error:', error);
-    if (error.error instanceof Error) {
-      let errMessage = error.error.message;
-      return Observable.throw(errMessage);
-    }
-    return Observable.throw(error || 'ASP.NET Core server error');
-  }
-}
Index: Farmatiko/ClientApp/src/app/shared/core.module.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/core.module.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/core.module.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,33 @@
+import { NgModule, APP_INITIALIZER, Optional, SkipSelf } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { AuthService } from './services/auth.service';
+import { appInitializer } from './services/app-initializer';
+import { JwtInterceptor } from './interceptors/jwt.interceptor';
+import { UnauthorizedInterceptor } from './interceptors/unauthorized.interceptor';
+
+@NgModule({
+  declarations: [],
+  imports: [CommonModule],
+  providers: [
+    {
+      provide: APP_INITIALIZER,
+      useFactory: appInitializer,
+      multi: true,
+      deps: [AuthService],
+    },
+    { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
+    {
+      provide: HTTP_INTERCEPTORS,
+      useClass: UnauthorizedInterceptor,
+      multi: true,
+    },
+  ],
+})
+export class CoreModule {
+  constructor(@Optional() @SkipSelf() core: CoreModule) {
+    if (core) {
+      throw new Error('Core Module can only be imported to AppModule.');
+    }
+  }
+}
Index: rmatiko/ClientApp/src/app/shared/data.service.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/data.service.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ 	(revision )
@@ -1,194 +1,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient, HttpErrorResponse } from '@angular/common/http';
-
-import { Observable } from 'rxjs';
-import { map, catchError } from 'rxjs/operators';
-
-import { IHealthFacilities, IHealthcareWorkers, IMedicine, IPandemic, IPharmacy, IPharmacyHead, IPharmacyHeadRequest } from './interfaces';
-
-@Injectable()
-export class DataService {
-    baseFacilitiesUrl: string = '/api/facilities';
-    baseWorkersUrl: string = '/api/workers';
-    baseMedicineUrl: string = '/api/medicines';
-    basePandemicUrl: string = '/api/pandemic';
-    basePharmacyUrl: string = '/api/pharmacy';
-    basePharmacyHead: string = '/api/pharmacyhead';
-
-    constructor(private http: HttpClient) {
-
-    }
-
-    //Facility GET
-    getFacilities() : Observable<IHealthFacilities[]> {
-        return this.http.get<IHealthFacilities[]>(this.baseFacilitiesUrl)
-                   .pipe(
-                        map((facilities: IHealthFacilities[]) => {
-                            return facilities;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    searchFacilities(searchquery: string) : Observable<IHealthFacilities[]> {
-        return this.http.get<IHealthFacilities[]>(this.baseFacilitiesUrl + '/search/' + searchquery)
-                   .pipe(
-                        map((facilities: IHealthFacilities[]) => {
-                            return facilities;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    getFacility(id: string) : Observable<IHealthFacilities> {
-        return this.http.get<IHealthFacilities>(this.baseFacilitiesUrl + '/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-
-
-    //Worker GET
-    getWorkers() : Observable<IHealthcareWorkers[]> {
-        return this.http.get<IHealthcareWorkers[]>(this.baseWorkersUrl)
-                   .pipe(
-                        map((workers: IHealthcareWorkers[]) => {
-                            return workers;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    searchWorkers(searchquery: string) : Observable<IHealthcareWorkers[]> {
-        return this.http.get<IHealthcareWorkers[]>(this.baseWorkersUrl + '/search/' + searchquery)
-                   .pipe(
-                        map((workers: IHealthcareWorkers[]) => {
-                            return workers;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    getWorker(id: string) : Observable<IHealthcareWorkers> {
-        return this.http.get<IHealthcareWorkers>(this.baseWorkersUrl + '/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-
-
-    //Medicine GET
-    getMedicines() : Observable<IMedicine[]> {
-        return this.http.get<IMedicine[]>(this.baseMedicineUrl)
-                   .pipe(
-                        map((medicines: IMedicine[]) => {
-                            return medicines;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    searchMedicines(searchquery: string) : Observable<IMedicine[]> {
-        return this.http.get<IMedicine[]>(this.baseMedicineUrl + '/search/' + searchquery)
-                   .pipe(
-                        map((medicines: IMedicine[]) => {
-                            return medicines;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    getMedicine(id: string) : Observable<IMedicine> {
-        return this.http.get<IMedicine>(this.baseMedicineUrl + '/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-
-
-    getPandemic() : Observable<IPandemic> {
-        return this.http.get<IPandemic>(this.basePandemicUrl)
-                   .pipe(catchError(this.handleError));
-    }
-
-
-    //Pharmacy GET
-    getPharmacies() : Observable<IPharmacy[]> {
-        return this.http.get<IPharmacy[]>(this.basePharmacyUrl)
-                   .pipe(
-                        map((pharmacies: IPharmacy[]) => {
-                            return pharmacies;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    searchPharmacies(searchquery: string) : Observable<IPharmacy[]> {
-        return this.http.get<IPharmacy[]>(this.basePharmacyUrl + '/search/' + searchquery)
-                   .pipe(
-                        map((pharmacies: IPharmacy[]) => {
-                            return pharmacies;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    getPharmacy(id: string) : Observable<IPharmacy> {
-        return this.http.get<IPharmacy>(this.basePharmacyUrl + '/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-
-    //PharmacyHead GET
-    getPharmacyHeads() : Observable<IPharmacyHead[]> {
-        return this.http.get<IPharmacyHead[]>(this.basePharmacyHead)
-                   .pipe(
-                        map((pharmacyheads: IPharmacyHead[]) => {
-                            return pharmacyheads;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    getClaimingRequests() : Observable<IPharmacyHeadRequest[]> {
-        return this.http.get<IPharmacyHeadRequest[]>(this.basePharmacyHead + '/requests')
-                   .pipe(
-                        map((requests: IPharmacyHeadRequest[]) => {
-                            return requests;
-                        }),
-                        catchError(this.handleError)
-                   );
-    }
-    //PharmacyHead POST
-    insertPharmacyHead(head: IPharmacyHead) : Observable<IPharmacyHead> {
-        return this.http.post<IPharmacyHead>(this.basePharmacyHead + '/add', head)
-                   .pipe(
-                        map((data) => {
-                            return data;
-                        }),
-                        catchError(this.handleError)
-                    );
-    }
-    claimPharmacy(req: IPharmacyHeadRequest) : Observable<IPharmacyHeadRequest> {
-        return this.http.post<IPharmacyHeadRequest>(this.basePharmacyHead + '/requests', req)
-                    .pipe(
-                        map((data) => {
-                            return data;
-                        }),
-                        catchError(this.handleError)
-                    );
-    }
-    //PharmacyHead PUT
-    updatePharmacyHead(head: IPharmacyHead) : Observable<IPharmacyHead> {
-        return this.http.put<IPharmacyHead>(this.basePharmacyHead + '/' + head.id, head)
-                   .pipe(
-                        map((data) => {
-                            return data;
-                        }),
-                        catchError(this.handleError)
-                    );
-    }
-    //PharmacyHead DELETE
-    deletePharmacyHead(id: string) : Observable<boolean> {
-        return this.http.delete<boolean>(this.basePharmacyHead + '/delete/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-    deleteClaimingRequest(id: string) : Observable<boolean> {
-        return this.http.delete<boolean>(this.basePharmacyHead + '/requests/' + id)
-                   .pipe(catchError(this.handleError));
-    }
-
-    private handleError(error: HttpErrorResponse) {
-        console.error('server error:', error);
-        if (error.error instanceof Error) {
-          let errMessage = error.error.message;
-          return Observable.throw(errMessage);
-        }
-        return Observable.throw(error || 'ASP.NET Core server error');
-    }
-
-}
Index: Farmatiko/ClientApp/src/app/shared/guards/auth.guard.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/guards/auth.guard.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/guards/auth.guard.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,40 @@
+import { Injectable } from '@angular/core';
+import {
+  CanActivate,
+  ActivatedRouteSnapshot,
+  RouterStateSnapshot,
+  UrlTree,
+  Router,
+} from '@angular/router';
+import { Observable } from 'rxjs';
+import { AuthService } from '../services/auth.service';
+import { map } from 'rxjs/operators';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class AuthGuard implements CanActivate {
+  constructor(private router: Router, private authService: AuthService) {}
+
+  canActivate(
+    next: ActivatedRouteSnapshot,
+    state: RouterStateSnapshot
+  ):
+    | Observable<boolean | UrlTree>
+    | Promise<boolean | UrlTree>
+    | boolean
+    | UrlTree {
+    return this.authService.user$.pipe(
+      map((user) => {
+        if (user) {
+          return true;
+        } else {
+          this.router.navigate(['login'], {
+            queryParams: { returnUrl: state.url },
+          });
+          return false;
+        }
+      })
+    );
+  }
+}
Index: Farmatiko/ClientApp/src/app/shared/interceptors/jwt.interceptor.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/interceptors/jwt.interceptor.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/interceptors/jwt.interceptor.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+import {
+  HttpRequest,
+  HttpHandler,
+  HttpEvent,
+  HttpInterceptor,
+} from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { AuthService } from '../services/auth.service';
+import { environment } from '../../../environments/environment';
+
+@Injectable()
+export class JwtInterceptor implements HttpInterceptor {
+  constructor(private authService: AuthService) {}
+
+  intercept(
+    request: HttpRequest<unknown>,
+    next: HttpHandler
+  ): Observable<HttpEvent<unknown>> {
+    const accessToken = localStorage.getItem('access_token');
+    const isApiUrl = request.url.startsWith(environment.baseApiUrl);
+    if (accessToken && isApiUrl) {
+      request = request.clone({
+        setHeaders: { Authorization: `Bearer ${accessToken}` },
+      });
+    }
+
+    return next.handle(request);
+  }
+}
Index: Farmatiko/ClientApp/src/app/shared/interceptors/unauthorized.interceptor.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/interceptors/unauthorized.interceptor.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/interceptors/unauthorized.interceptor.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,39 @@
+import { Injectable } from '@angular/core';
+import {
+  HttpRequest,
+  HttpHandler,
+  HttpEvent,
+  HttpInterceptor,
+} from '@angular/common/http';
+import { Observable, throwError } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+import { AuthService } from '../services/auth.service';
+import { environment } from '../../../environments/environment';
+import { Router } from '@angular/router';
+
+@Injectable()
+export class UnauthorizedInterceptor implements HttpInterceptor {
+  constructor(private authService: AuthService, private router: Router) {}
+
+  intercept(
+    request: HttpRequest<unknown>,
+    next: HttpHandler
+  ): Observable<HttpEvent<unknown>> {
+    return next.handle(request).pipe(
+      catchError((err) => {
+        if (err.status === 401) {
+          this.authService.clearLocalStorage();
+          this.router.navigate(['login'], {
+            queryParams: { returnUrl: this.router.routerState.snapshot.url },
+          });
+        }
+
+        if (!environment.production) {
+          console.error(err);
+        }
+        const error = (err && err.error && err.error.message) || err.statusText;
+        return throwError(error);
+      })
+    );
+  }
+}
Index: Farmatiko/ClientApp/src/app/shared/interfaces.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/interfaces.ts	(revision 993189ebdd8ea7f88583498d4188a29688f0f069)
+++ Farmatiko/ClientApp/src/app/shared/interfaces.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -55,4 +55,5 @@
     Passwd: string;
     Name: string;
+    Role?: string;
 }  
 
Index: Farmatiko/ClientApp/src/app/shared/services/app-initializer.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/services/app-initializer.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/services/app-initializer.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,9 @@
+import { AuthService } from './auth.service';
+
+export function appInitializer(authService: AuthService) {
+  return () =>
+    new Promise((resolve) => {
+      console.log('refresh token on app start up')
+      authService.refreshToken().subscribe().add(resolve);
+    });
+}
Index: Farmatiko/ClientApp/src/app/shared/services/auth.service.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/services/auth.service.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/services/auth.service.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,151 @@
+import { Injectable, OnDestroy } from '@angular/core';
+import { Router } from '@angular/router';
+import { HttpClient } from '@angular/common/http';
+import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
+import { map, tap, delay, finalize } from 'rxjs/operators';
+import { IPharmacyHead } from '../interfaces';
+import { environment } from '../../../environments/environment';
+
+
+interface LoginResult {
+  username: string;
+  role: string;
+  accessToken: string;
+  refreshToken: string;
+  head: IPharmacyHead;
+}
+
+@Injectable({
+  providedIn: 'root',
+})
+export class AuthService implements OnDestroy {
+  private readonly apiUrl = `${environment.baseApiUrl}api/pharmacyhead`;
+  private timer: Subscription;
+  private _user = new BehaviorSubject<IPharmacyHead>(null);
+  user$: Observable<IPharmacyHead> = this._user.asObservable();
+
+  private storageEventListener(event: StorageEvent) {
+    if (event.storageArea === localStorage) {
+      if (event.key === 'logout-event') {
+        this.stopTokenTimer();
+        this._user.next(null);
+      }
+      if (event.key === 'login-event') {
+        this.stopTokenTimer();
+        this.http.get<LoginResult>(`${this.apiUrl}/user`).subscribe((x) => {
+          this._user.next({
+            Email: x.username,
+            Role: x.role,
+            Passwd: x.head.Passwd,
+            Name: x.head.Name
+          });
+        });
+      }
+    }
+  }
+
+  constructor(private router: Router, private http: HttpClient) {
+    window.addEventListener('storage', this.storageEventListener.bind(this));
+    console.log(this.apiUrl);
+  }
+
+  ngOnDestroy(): void {
+    window.removeEventListener('storage', this.storageEventListener.bind(this));
+  }
+
+  login(username: string, password: string) {
+    return this.http
+      .post<LoginResult>(`${this.apiUrl}/login`, { username, password })
+      .pipe(
+        map((x) => {
+          this._user.next({
+            Email: x.username,
+            Role: x.role,
+            Passwd: x.head.Passwd,
+            Name: x.head.Name
+          });
+          this.setLocalStorage(x);
+          this.startTokenTimer();
+          return x;
+        })
+      );
+  }
+
+  logout() {
+    this.http
+      .post<unknown>(`${this.apiUrl}/logout`, {})
+      .pipe(
+        finalize(() => {
+          this.clearLocalStorage();
+          this._user.next(null);
+          this.stopTokenTimer();
+          this.router.navigate(['login']);
+        })
+      )
+      .subscribe();
+  }
+
+  refreshToken() {
+    const refreshToken = localStorage.getItem('refresh_token');
+    if (!refreshToken) {
+      this.clearLocalStorage();
+      return of(null);
+    }
+
+    return this.http
+      .post<LoginResult>(`${this.apiUrl}/refresh-token`, { refreshToken })
+      .pipe(
+        map((x) => {
+          this._user.next({
+            Email: x.username,
+            Role: x.role,
+            Passwd: x.head.Passwd,
+            Name: x.head.Name
+          });
+          this.setLocalStorage(x);
+          this.startTokenTimer();
+          return x;
+        })
+      );
+  }
+
+  setLocalStorage(x: LoginResult) {
+    localStorage.setItem('access_token', x.accessToken);
+    localStorage.setItem('refresh_token', x.refreshToken);
+    localStorage.setItem('login-event', 'login' + Math.random());
+  }
+
+  clearLocalStorage() {
+    localStorage.removeItem('access_token');
+    localStorage.removeItem('refresh_token');
+    localStorage.setItem('logout-event', 'logout' + Math.random());
+  }
+
+  getUser() {
+    return this.user$;
+  }
+
+  private getTokenRemainingTime() {
+    const accessToken = localStorage.getItem('access_token');
+    if (!accessToken) {
+      return 0;
+    }
+    const jwtToken = JSON.parse(atob(accessToken.split('.')[1]));
+    const expires = new Date(jwtToken.exp * 1000);
+    return expires.getTime() - Date.now();
+  }
+
+  private startTokenTimer() {
+    const timeout = this.getTokenRemainingTime();
+    this.timer = of(true)
+      .pipe(
+        delay(timeout),
+        tap(() => this.refreshToken().subscribe())
+      )
+      .subscribe();
+  }
+
+  private stopTokenTimer() {
+    this.timer?.unsubscribe();
+  }
+}
Index: Farmatiko/ClientApp/src/app/shared/services/data.service.ts
===================================================================
--- Farmatiko/ClientApp/src/app/shared/services/data.service.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
+++ Farmatiko/ClientApp/src/app/shared/services/data.service.ts	(revision 1f4846d43046b2d1c3d0e073a7646afd3ea15785)
@@ -0,0 +1,194 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+
+import { IHealthFacilities, IHealthcareWorkers, IMedicine, IPandemic, IPharmacy, IPharmacyHead, IPharmacyHeadRequest } from '../interfaces';
+
+@Injectable()
+export class DataService {
+    baseFacilitiesUrl: string = '/api/facilities';
+    baseWorkersUrl: string = '/api/workers';
+    baseMedicineUrl: string = '/api/medicines';
+    basePandemicUrl: string = '/api/pandemic';
+    basePharmacyUrl: string = '/api/pharmacy';
+    basePharmacyHead: string = '/api/pharmacyhead';
+
+    constructor(private http: HttpClient) {
+
+    }
+
+    //Facility GET
+    getFacilities() : Observable<IHealthFacilities[]> {
+        return this.http.get<IHealthFacilities[]>(this.baseFacilitiesUrl)
+                   .pipe(
+                        map((facilities: IHealthFacilities[]) => {
+                            return facilities;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    searchFacilities(searchquery: string) : Observable<IHealthFacilities[]> {
+        return this.http.get<IHealthFacilities[]>(this.baseFacilitiesUrl + '/search/' + searchquery)
+                   .pipe(
+                        map((facilities: IHealthFacilities[]) => {
+                            return facilities;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    getFacility(id: string) : Observable<IHealthFacilities> {
+        return this.http.get<IHealthFacilities>(this.baseFacilitiesUrl + '/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+
+
+    //Worker GET
+    getWorkers() : Observable<IHealthcareWorkers[]> {
+        return this.http.get<IHealthcareWorkers[]>(this.baseWorkersUrl)
+                   .pipe(
+                        map((workers: IHealthcareWorkers[]) => {
+                            return workers;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    searchWorkers(searchquery: string) : Observable<IHealthcareWorkers[]> {
+        return this.http.get<IHealthcareWorkers[]>(this.baseWorkersUrl + '/search/' + searchquery)
+                   .pipe(
+                        map((workers: IHealthcareWorkers[]) => {
+                            return workers;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    getWorker(id: string) : Observable<IHealthcareWorkers> {
+        return this.http.get<IHealthcareWorkers>(this.baseWorkersUrl + '/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+
+
+    //Medicine GET
+    getMedicines() : Observable<IMedicine[]> {
+        return this.http.get<IMedicine[]>(this.baseMedicineUrl)
+                   .pipe(
+                        map((medicines: IMedicine[]) => {
+                            return medicines;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    searchMedicines(searchquery: string) : Observable<IMedicine[]> {
+        return this.http.get<IMedicine[]>(this.baseMedicineUrl + '/search/' + searchquery)
+                   .pipe(
+                        map((medicines: IMedicine[]) => {
+                            return medicines;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    getMedicine(id: string) : Observable<IMedicine> {
+        return this.http.get<IMedicine>(this.baseMedicineUrl + '/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+
+
+    getPandemic() : Observable<IPandemic> {
+        return this.http.get<IPandemic>(this.basePandemicUrl)
+                   .pipe(catchError(this.handleError));
+    }
+
+
+    //Pharmacy GET
+    getPharmacies() : Observable<IPharmacy[]> {
+        return this.http.get<IPharmacy[]>(this.basePharmacyUrl)
+                   .pipe(
+                        map((pharmacies: IPharmacy[]) => {
+                            return pharmacies;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    searchPharmacies(searchquery: string) : Observable<IPharmacy[]> {
+        return this.http.get<IPharmacy[]>(this.basePharmacyUrl + '/search/' + searchquery)
+                   .pipe(
+                        map((pharmacies: IPharmacy[]) => {
+                            return pharmacies;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    getPharmacy(id: string) : Observable<IPharmacy> {
+        return this.http.get<IPharmacy>(this.basePharmacyUrl + '/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+
+    //PharmacyHead GET
+    getPharmacyHeads() : Observable<IPharmacyHead[]> {
+        return this.http.get<IPharmacyHead[]>(this.basePharmacyHead)
+                   .pipe(
+                        map((pharmacyheads: IPharmacyHead[]) => {
+                            return pharmacyheads;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    getClaimingRequests() : Observable<IPharmacyHeadRequest[]> {
+        return this.http.get<IPharmacyHeadRequest[]>(this.basePharmacyHead + '/requests')
+                   .pipe(
+                        map((requests: IPharmacyHeadRequest[]) => {
+                            return requests;
+                        }),
+                        catchError(this.handleError)
+                   );
+    }
+    //PharmacyHead POST
+    insertPharmacyHead(head: IPharmacyHead) : Observable<IPharmacyHead> {
+        return this.http.post<IPharmacyHead>(this.basePharmacyHead + '/add', head)
+                   .pipe(
+                        map((data) => {
+                            return data;
+                        }),
+                        catchError(this.handleError)
+                    );
+    }
+    claimPharmacy(req: IPharmacyHeadRequest) : Observable<IPharmacyHeadRequest> {
+        return this.http.post<IPharmacyHeadRequest>(this.basePharmacyHead + '/requests', req)
+                    .pipe(
+                        map((data) => {
+                            return data;
+                        }),
+                        catchError(this.handleError)
+                    );
+    }
+    //PharmacyHead PUT
+    updatePharmacyHead(head: IPharmacyHead) : Observable<IPharmacyHead> {
+        return this.http.put<IPharmacyHead>(this.basePharmacyHead + '/' + head.id, head)
+                   .pipe(
+                        map((data) => {
+                            return data;
+                        }),
+                        catchError(this.handleError)
+                    );
+    }
+    //PharmacyHead DELETE
+    deletePharmacyHead(id: string) : Observable<boolean> {
+        return this.http.delete<boolean>(this.basePharmacyHead + '/delete/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+    deleteClaimingRequest(id: string) : Observable<boolean> {
+        return this.http.delete<boolean>(this.basePharmacyHead + '/requests/' + id)
+                   .pipe(catchError(this.handleError));
+    }
+
+    private handleError(error: HttpErrorResponse) {
+        console.error('server error:', error);
+        if (error.error instanceof Error) {
+          let errMessage = error.error.message;
+          return Observable.throw(errMessage);
+        }
+        return Observable.throw(error || 'ASP.NET Core server error');
+    }
+
+}
