Added admin page
This commit is contained in:
		
							
								
								
									
										58
									
								
								src/app/admin/admin.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/app/admin/admin.component.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | <div class="w-100"> | ||||||
|  |     <div class="container d-flex align-items-center" style="height: 300px"> | ||||||
|  |         <div class="w-100 text-center"> | ||||||
|  |             <typewriter text="Admin Panel" [delay]="500"></typewriter> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="container mb-md-5 p-0 bg-white"> | ||||||
|  |         <div class="p-4"> | ||||||
|  |             <h3>Users</h3> | ||||||
|  |             <table class="table mt-3"> | ||||||
|  |                 <thead> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>First Name</th> | ||||||
|  |                     <th>Last Name</th> | ||||||
|  |                     <th>Email</th> | ||||||
|  |                     <th class="text-center">Admin</th> | ||||||
|  |                 </tr> | ||||||
|  |                 </thead> | ||||||
|  |                 <tbody> | ||||||
|  |                 <tr *ngFor="let user of adminService.users | async"> | ||||||
|  |                     <td>{{user.firstName}}</td> | ||||||
|  |                     <td>{{user.lastName}}</td> | ||||||
|  |                     <td>{{user.email}}</td> | ||||||
|  |                     <td class="text-center"> | ||||||
|  |                         <div class="form-check"> | ||||||
|  |                             <input class="form-check-input" type="checkbox" id="adminCheck" [checked]="user.admin" | ||||||
|  |                                    disabled> | ||||||
|  |                         </div> | ||||||
|  |                     </td> | ||||||
|  |                 </tr> | ||||||
|  |                 </tbody> | ||||||
|  |             </table> | ||||||
|  |         </div> | ||||||
|  |         <div class="p-4"> | ||||||
|  |             <h3>Quotes</h3> | ||||||
|  |             <table class="table mt-3"> | ||||||
|  |                 <thead> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>Quote</th> | ||||||
|  |                 </tr> | ||||||
|  |                 </thead> | ||||||
|  |                 <tbody> | ||||||
|  |                 <tr *ngFor="let quote of store.quotes | async"> | ||||||
|  |                     <td>{{quote.text}}</td> | ||||||
|  |                 </tr> | ||||||
|  |                 <tr> | ||||||
|  |                     <td> | ||||||
|  |                         <a routerLink="">+ Add Quote</a> | ||||||
|  |                     </td> | ||||||
|  |                 </tr> | ||||||
|  |                 </tbody> | ||||||
|  |             </table> | ||||||
|  |         </div> | ||||||
|  |         <footer class="p-1 bg-dark text-center" style="color: grey"> | ||||||
|  |             © 2019 ZaksCode | ||||||
|  |         </footer> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										11
									
								
								src/app/admin/admin.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/app/admin/admin.component.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | import {Component} from '@angular/core'; | ||||||
|  | import {AdminService} from './admin.service'; | ||||||
|  | import {AppStore} from '../app.store'; | ||||||
|  |  | ||||||
|  | @Component({ | ||||||
|  |     selector: 'admin', | ||||||
|  |     templateUrl: 'admin.component.html' | ||||||
|  | }) | ||||||
|  | export class AdminComponent { | ||||||
|  |     constructor(public adminService: AdminService, public store: AppStore) {} | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/app/admin/admin.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/app/admin/admin.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | import {Observable} from 'rxjs'; | ||||||
|  | import {DbUser} from '../models/dbUser'; | ||||||
|  | import {AngularFirestore} from '@angular/fire/firestore'; | ||||||
|  | import {Injectable} from '@angular/core'; | ||||||
|  |  | ||||||
|  | @Injectable({ | ||||||
|  |     providedIn: 'root' | ||||||
|  | }) | ||||||
|  | export class AdminService { | ||||||
|  |     users: Observable<DbUser[]>; | ||||||
|  |  | ||||||
|  |     constructor(private firestore: AngularFirestore) { | ||||||
|  |         this.users = this.firestore.collection<DbUser>('users').valueChanges(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1 +1,10 @@ | |||||||
|  | <div style="position: absolute; top: 1em; right: 1em"> | ||||||
|  |     <div *ngIf="loggedIn"> | ||||||
|  |         <a routerLink="/admin" class="mr-3">Admin Panel</a> | ||||||
|  |         <a routerLink="" (click)="authService.logout()">Logout</a> | ||||||
|  |     </div> | ||||||
|  |     <div *ngIf="!loggedIn"> | ||||||
|  |         <a routerLink="" (click)="authService.googleLogin()">Login</a> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
| <router-outlet></router-outlet> | <router-outlet></router-outlet> | ||||||
|   | |||||||
| @@ -1,11 +1,20 @@ | |||||||
| import {Component} from '@angular/core'; | import {Component} from '@angular/core'; | ||||||
|  | import {AuthService} from './auth.service'; | ||||||
|  | import {ActivatedRoute} from '@angular/router'; | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
|     selector: 'app-root', |     selector: 'app-root', | ||||||
|     templateUrl: 'app.component.html' |     templateUrl: 'app.component.html' | ||||||
| }) | }) | ||||||
| export class AppComponent { | export class AppComponent { | ||||||
|  |     loggedIn = false; | ||||||
|  |  | ||||||
|     set title(title: string) { |     set title(title: string) { | ||||||
|         document.getElementsByTagName('title')[0].innerHTML = `Zaks Code${title ? ` - ${title}` : ''}`; |         document.getElementsByTagName('title')[0].innerHTML = `Zaks Code${title ? ` - ${title}` : ''}`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     constructor(private route: ActivatedRoute, public authService: AuthService) { | ||||||
|  |         this.route.url.subscribe(() => this.title = ''); // Clear the title on nav event | ||||||
|  |         this.authService.user.subscribe(user => this.loggedIn = !!user); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,15 +12,19 @@ import {SlideShowComponent} from './components/slideShow/slideShow.component'; | |||||||
| import {environment} from '../environments/environment'; | import {environment} from '../environments/environment'; | ||||||
| import {AppComponent} from './app.component'; | import {AppComponent} from './app.component'; | ||||||
| import {AppRouting} from './app.routing'; | import {AppRouting} from './app.routing'; | ||||||
|  | import {AdminComponent} from './admin/admin.component'; | ||||||
|  | import {AngularFireAuthModule} from '@angular/fire/auth'; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     declarations: [ |     declarations: [ | ||||||
|  |         AdminComponent, | ||||||
|         AppComponent, |         AppComponent, | ||||||
|         HomeComponent, |         HomeComponent, | ||||||
|         SlideShowComponent, |         SlideShowComponent, | ||||||
|         TypewriterComponent |         TypewriterComponent | ||||||
|     ], |     ], | ||||||
|     imports: [ |     imports: [ | ||||||
|  |         AngularFireAuthModule, | ||||||
|         AngularFireModule.initializeApp(environment.firebase), |         AngularFireModule.initializeApp(environment.firebase), | ||||||
|         AngularFirestoreModule, |         AngularFirestoreModule, | ||||||
|         AppRouting, |         AppRouting, | ||||||
|   | |||||||
| @@ -1,11 +1,14 @@ | |||||||
| import {NgModule} from '@angular/core'; | import {NgModule} from '@angular/core'; | ||||||
| import {RouterModule} from '@angular/router'; | import {RouterModule} from '@angular/router'; | ||||||
| import {HomeComponent} from './home/home.component'; | import {HomeComponent} from './home/home.component'; | ||||||
|  | import {AdminComponent} from './admin/admin.component'; | ||||||
|  | import {AuthService} from './auth.service'; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     imports: [ |     imports: [ | ||||||
|         RouterModule.forRoot([ |         RouterModule.forRoot([ | ||||||
|             {path: '', component: HomeComponent}, |             {path: '', component: HomeComponent}, | ||||||
|  |             {path: 'admin', component: AdminComponent, canActivate: [AuthService]}, | ||||||
|             {path: '**', redirectTo: ''} |             {path: '**', redirectTo: ''} | ||||||
|         ]) |         ]) | ||||||
|     ], |     ], | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| import {Injectable} from '@angular/core'; | import {Injectable} from '@angular/core'; | ||||||
| import {Observable} from 'rxjs'; | import {BehaviorSubject, Observable} from 'rxjs'; | ||||||
| import {AngularFirestore} from '@angular/fire/firestore'; | import {AngularFirestore} from '@angular/fire/firestore'; | ||||||
| import {Quote} from './models/quote'; | import {Quote} from './models/quote'; | ||||||
|  | import {DbUser} from './models/dbUser'; | ||||||
|  |  | ||||||
| @Injectable({ | @Injectable({ | ||||||
|     providedIn: 'root' |     providedIn: 'root' | ||||||
| @@ -12,4 +13,13 @@ export class AppStore { | |||||||
|     constructor(private firestore: AngularFirestore) { |     constructor(private firestore: AngularFirestore) { | ||||||
|         this.quotes = this.firestore.collection<Quote>('quotes').valueChanges(); |         this.quotes = this.firestore.collection<Quote>('quotes').valueChanges(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     addQuote(text: string) { | ||||||
|  |         return this.firestore.collection<Quote>('quotes').add({text: text}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async getUser(uid: string) { | ||||||
|  |         let user = await this.firestore.collection<DbUser>('users').doc(uid).get().toPromise(); | ||||||
|  |         return user.data(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								src/app/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/app/auth.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import {Injectable} from '@angular/core'; | ||||||
|  | import {AngularFireAuth} from '@angular/fire/auth'; | ||||||
|  | import {CanActivate, Router} from '@angular/router'; | ||||||
|  | import {BehaviorSubject, Observable} from 'rxjs'; | ||||||
|  | import { auth } from 'firebase'; | ||||||
|  |  | ||||||
|  | import {map} from 'rxjs/operators'; | ||||||
|  | import {User} from './models/user'; | ||||||
|  | import {AppStore} from './app.store'; | ||||||
|  | import {tap} from 'rxjs/internal/operators/tap'; | ||||||
|  | import {filter} from 'rxjs/internal/operators/filter'; | ||||||
|  |  | ||||||
|  | @Injectable({ | ||||||
|  |     providedIn: 'root' | ||||||
|  | }) | ||||||
|  | export class AuthService implements CanActivate { | ||||||
|  |     user = new BehaviorSubject<User>(null); | ||||||
|  |  | ||||||
|  |     constructor(private store: AppStore, private afAuth: AngularFireAuth, private router: Router) { | ||||||
|  |         this.afAuth.user.subscribe(async user => { | ||||||
|  |             this.user.next(user ? <User>Object.assign(await this.store.getUser(user.uid), user): null); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     canActivate(): Observable<boolean> { | ||||||
|  |         return this.user.pipe( | ||||||
|  |             filter(user => !!user), | ||||||
|  |             map(user => !user.isAnonymous), | ||||||
|  |             tap(user => user ? null : this.googleLogin()) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     googleLogin() { | ||||||
|  |         return this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async logout() { | ||||||
|  |         await this.afAuth.auth.signOut(); | ||||||
|  |         this.router.navigate(['/']); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -9,8 +9,8 @@ import {filter, map} from 'rxjs/operators'; | |||||||
|     styleUrls: ['typewriter.component.scss'] |     styleUrls: ['typewriter.component.scss'] | ||||||
| }) | }) | ||||||
| export class TypewriterComponent { | export class TypewriterComponent { | ||||||
|     readonly delay = 1500; |     @Input() delay = 1500; | ||||||
|     readonly speed = 100; |     @Input() speed = 100; | ||||||
|  |  | ||||||
|     @Input() |     @Input() | ||||||
|     set text(text: string) { |     set text(text: string) { | ||||||
|   | |||||||
| @@ -18,8 +18,7 @@ | |||||||
|                             <div><i class="mr-2 fa fa-map-marker-alt"></i> London Ontario, Canada</div> |                             <div><i class="mr-2 fa fa-map-marker-alt"></i> London Ontario, Canada</div> | ||||||
|                             <div><i class="mr-2 fa fa-envelope"></i> <a href="mailto:zaktimson@gmail.com">zaktimson@gmail.com</a> |                             <div><i class="mr-2 fa fa-envelope"></i> <a href="mailto:zaktimson@gmail.com">zaktimson@gmail.com</a> | ||||||
|                             </div> |                             </div> | ||||||
|                             <div><i class="mr-2 fab fa-github"></i> <a href="https://github.com/ztimson" |                             <div><i class="mr-2 fab fa-github"></i> <a href="https://github.com/ztimson" target="_blank">github.com/ztimson</a></div> | ||||||
|                                                                        target="_blank">github.com/ztimson</a></div> |  | ||||||
|                         </div> |                         </div> | ||||||
|                     </div> |                     </div> | ||||||
|                 </mat-card-content> |                 </mat-card-content> | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								src/app/models/dbUser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/app/models/dbUser.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | export interface DbUser { | ||||||
|  |     admin: boolean; | ||||||
|  |     email: string; | ||||||
|  |     firstName: string; | ||||||
|  |     lastName: string; | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								src/app/models/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/models/user.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | import {User as FirebaseUser} from 'firebase'; | ||||||
|  | import {DbUser} from './dbUser'; | ||||||
|  |  | ||||||
|  | export interface User extends FirebaseUser, DbUser { } | ||||||
		Reference in New Issue
	
	Block a user