Add auth, nav, and home page
This commit is contained in:
parent
b1e2ff4813
commit
d678707dc5
47
src/my-prayer-journal/package-lock.json
generated
47
src/my-prayer-journal/package-lock.json
generated
@ -500,6 +500,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/router/-/router-9.1.9.tgz",
|
||||||
"integrity": "sha512-4u+CWMPB4hCkAsFCEzC94YEWT0wVozqGkc/Dortt2hFaqvZpIegg6iJVZlDxuyDjzFYBPnnbTDdgiTTA8ckfuA=="
|
"integrity": "sha512-4u+CWMPB4hCkAsFCEzC94YEWT0wVozqGkc/Dortt2hFaqvZpIegg6iJVZlDxuyDjzFYBPnnbTDdgiTTA8ckfuA=="
|
||||||
},
|
},
|
||||||
|
"@auth0/auth0-spa-js": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-p+9m13k8MMpIthoWso7pXxaEZq5tAon8GPUPU+Wu4hgDQyJZIzqtCMHcQT0qdzfK/KoZwmoifj4aOvJxKO9I4Q==",
|
||||||
|
"requires": {
|
||||||
|
"abortcontroller-polyfill": "^1.4.0",
|
||||||
|
"browser-tabs-lock": "^1.2.8",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"es-cookie": "^1.3.2",
|
||||||
|
"fast-text-encoding": "^1.0.1",
|
||||||
|
"promise-polyfill": "^8.1.3",
|
||||||
|
"unfetch": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.10.1",
|
"version": "7.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
|
||||||
@ -1940,6 +1954,11 @@
|
|||||||
"through": ">=2.2.7 <3"
|
"through": ">=2.2.7 <3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"abortcontroller-polyfill": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-3ZFfCRfDzx3GFjO6RAkYx81lPGpUS20ISxux9gLxuKnqafNcFQo59+IoZqpO2WvQlyc287B62HDnDdNYRmlvWA=="
|
||||||
|
},
|
||||||
"accepts": {
|
"accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
@ -2617,6 +2636,11 @@
|
|||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"browser-tabs-lock": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-Xrj33YUTltPDoGrD1KnaAn5ZuxnnlJFcIW9srVTPHbMNPd9MlcnBCWaGV0STlvGKu8Ok0ad5qxyx5sIwFTr/Ig=="
|
||||||
|
},
|
||||||
"browserify-aes": {
|
"browserify-aes": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||||
@ -3520,8 +3544,7 @@
|
|||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "3.6.4",
|
"version": "3.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||||
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
|
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"core-js-compat": {
|
"core-js-compat": {
|
||||||
"version": "3.6.5",
|
"version": "3.6.5",
|
||||||
@ -4535,6 +4558,11 @@
|
|||||||
"string.prototype.trimright": "^2.1.1"
|
"string.prototype.trimright": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es-cookie": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q=="
|
||||||
|
},
|
||||||
"es-to-primitive": {
|
"es-to-primitive": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
|
||||||
@ -4906,6 +4934,11 @@
|
|||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fast-text-encoding": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
|
||||||
|
},
|
||||||
"fastparse": {
|
"fastparse": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||||
@ -9419,6 +9452,11 @@
|
|||||||
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"promise-polyfill": {
|
||||||
|
"version": "8.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.3.tgz",
|
||||||
|
"integrity": "sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g=="
|
||||||
|
},
|
||||||
"promise-retry": {
|
"promise-retry": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
|
||||||
@ -12078,6 +12116,11 @@
|
|||||||
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
|
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"unfetch": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg=="
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@angular/platform-browser": "~9.1.9",
|
"@angular/platform-browser": "~9.1.9",
|
||||||
"@angular/platform-browser-dynamic": "~9.1.9",
|
"@angular/platform-browser-dynamic": "~9.1.9",
|
||||||
"@angular/router": "~9.1.9",
|
"@angular/router": "~9.1.9",
|
||||||
|
"@auth0/auth0-spa-js": "^1.9.0",
|
||||||
"rxjs": "~6.5.4",
|
"rxjs": "~6.5.4",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router'
|
||||||
|
import { HomeComponent } from './pages/home/home.component'
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
const routes: Routes = [];
|
{ path: '', component: HomeComponent }
|
||||||
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
@ -5,10 +5,12 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
|||||||
import { AppRoutingModule } from './app-routing.module'
|
import { AppRoutingModule } from './app-routing.module'
|
||||||
import { AppComponent } from './app.component'
|
import { AppComponent } from './app.component'
|
||||||
import { SharedModule } from './shared/shared.module'
|
import { SharedModule } from './shared/shared.module'
|
||||||
|
import { HomeComponent } from './pages/home/home.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent
|
AppComponent,
|
||||||
|
HomeComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
16
src/my-prayer-journal/src/app/auth.service.spec.ts
Normal file
16
src/my-prayer-journal/src/app/auth.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { AuthService } from './auth.service'
|
||||||
|
|
||||||
|
describe('AuthService', () => {
|
||||||
|
let service: AuthService
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({})
|
||||||
|
service = TestBed.inject(AuthService)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
126
src/my-prayer-journal/src/app/auth.service.ts
Normal file
126
src/my-prayer-journal/src/app/auth.service.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Router } from '@angular/router'
|
||||||
|
import createAuth0Client, { GetUserOptions } from '@auth0/auth0-spa-js'
|
||||||
|
import Auth0Client from '@auth0/auth0-spa-js/dist/typings/Auth0Client'
|
||||||
|
import { BehaviorSubject, combineLatest, from, Observable, of, throwError } from 'rxjs'
|
||||||
|
import { catchError, concatMap, shareReplay, tap } from 'rxjs/operators'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
// Create an observable of Auth0 instance of client
|
||||||
|
auth0Client$ = (from(
|
||||||
|
createAuth0Client({
|
||||||
|
domain: "djs-consulting.auth0.com",
|
||||||
|
client_id: "Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n",
|
||||||
|
redirect_uri: `${window.location.origin}`
|
||||||
|
})
|
||||||
|
) as Observable<Auth0Client>).pipe(
|
||||||
|
shareReplay(1), // Every subscription receives the same shared value
|
||||||
|
catchError(err => throwError(err))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define observables for SDK methods that return promises by default
|
||||||
|
// For each Auth0 SDK method, first ensure the client instance is ready
|
||||||
|
// concatMap: Using the client instance, call SDK method; SDK returns a promise
|
||||||
|
// from: Convert that resulting promise into an observable
|
||||||
|
isAuthenticated$ = this.auth0Client$.pipe(
|
||||||
|
concatMap((client: Auth0Client) => from(client.isAuthenticated())),
|
||||||
|
tap(res => this.loggedIn = res)
|
||||||
|
)
|
||||||
|
handleRedirectCallback$ = this.auth0Client$.pipe(
|
||||||
|
concatMap((client: Auth0Client) => from(client.handleRedirectCallback()))
|
||||||
|
)
|
||||||
|
// Create subject and public observable of user profile data
|
||||||
|
private userProfileSubject$ = new BehaviorSubject<any>(null)
|
||||||
|
userProfile$ = this.userProfileSubject$.asObservable()
|
||||||
|
// Create a local property for login status
|
||||||
|
loggedIn = false
|
||||||
|
|
||||||
|
constructor(private router: Router) {
|
||||||
|
// On initial load, check authentication state with authorization server
|
||||||
|
// Set up local auth streams if user is already authenticated
|
||||||
|
this.localAuthSetup()
|
||||||
|
// Handle redirect from Auth0 login
|
||||||
|
this.handleAuthCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// When calling, options can be passed if desired
|
||||||
|
// https://auth0.github.io/auth0-spa-js/classes/auth0client.html#getuser
|
||||||
|
getUser$(options?: GetUserOptions): Observable<any> {
|
||||||
|
return this.auth0Client$.pipe(
|
||||||
|
concatMap((client: Auth0Client) => from(client.getUser(options))),
|
||||||
|
tap(user => this.userProfileSubject$.next(user))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private localAuthSetup() {
|
||||||
|
// This should only be called on app initialization
|
||||||
|
// Set up local authentication streams
|
||||||
|
const checkAuth$ = this.isAuthenticated$.pipe(
|
||||||
|
concatMap((loggedIn: boolean) => {
|
||||||
|
if (loggedIn) {
|
||||||
|
// If authenticated, get user and set in app
|
||||||
|
// NOTE: you could pass options here if needed
|
||||||
|
return this.getUser$()
|
||||||
|
}
|
||||||
|
// If not authenticated, return stream that emits 'false'
|
||||||
|
return of(loggedIn)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
checkAuth$.subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
login(redirectPath: string = '/') {
|
||||||
|
// A desired redirect path can be passed to login method
|
||||||
|
// (e.g., from a route guard)
|
||||||
|
// Ensure Auth0 client instance exists
|
||||||
|
this.auth0Client$.subscribe((client: Auth0Client) => {
|
||||||
|
// Call method to log in
|
||||||
|
client.loginWithRedirect({
|
||||||
|
redirect_uri: `${window.location.origin}`,
|
||||||
|
appState: { target: redirectPath }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleAuthCallback() {
|
||||||
|
// Call when app reloads after user logs in with Auth0
|
||||||
|
const params = window.location.search
|
||||||
|
if (params.includes('code=') && params.includes('state=')) {
|
||||||
|
let targetRoute: string // Path to redirect to after login processsed
|
||||||
|
const authComplete$ = this.handleRedirectCallback$.pipe(
|
||||||
|
// Have client, now call method to handle auth callback redirect
|
||||||
|
tap(cbRes => {
|
||||||
|
// Get and set target redirect route from callback results
|
||||||
|
targetRoute = cbRes.appState && cbRes.appState.target ? cbRes.appState.target : '/';
|
||||||
|
}),
|
||||||
|
concatMap(() => {
|
||||||
|
// Redirect callback complete; get user and login status
|
||||||
|
return combineLatest([
|
||||||
|
this.getUser$(),
|
||||||
|
this.isAuthenticated$
|
||||||
|
])
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// Subscribe to authentication completion observable
|
||||||
|
// Response will be an array of user and login status
|
||||||
|
authComplete$.subscribe(([user, loggedIn]) => {
|
||||||
|
// Redirect to target route after callback processing
|
||||||
|
this.router.navigate([targetRoute])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
// Ensure Auth0 client instance exists
|
||||||
|
this.auth0Client$.subscribe((client: Auth0Client) => {
|
||||||
|
// Call method to log out
|
||||||
|
client.logout({
|
||||||
|
client_id: "Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n",
|
||||||
|
returnTo: `${window.location.origin}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
13
src/my-prayer-journal/src/app/pages/home/home.component.html
Normal file
13
src/my-prayer-journal/src/app/pages/home/home.component.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<main role="main" class="mpj-main-content">
|
||||||
|
<p> </p>
|
||||||
|
<p>
|
||||||
|
myPrayerJournal is a place where individuals can record their prayer requests, record that they prayed for them,
|
||||||
|
update them as God moves in the situation, and record a final answer received on that request. It also allows
|
||||||
|
individuals to review their answered prayers.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This site is open and available to the general public. To get started, simply click the “Log On” link
|
||||||
|
above, and log on with either a Microsoft or Google account. You can also learn more about the site at the
|
||||||
|
“Docs” link, also above.
|
||||||
|
</p>
|
||||||
|
</main>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { HomeComponent } from './home.component'
|
||||||
|
|
||||||
|
describe('HomeComponent', () => {
|
||||||
|
let component: HomeComponent
|
||||||
|
let fixture: ComponentFixture<HomeComponent>
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ HomeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents()
|
||||||
|
}))
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HomeComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
15
src/my-prayer-journal/src/app/pages/home/home.component.ts
Normal file
15
src/my-prayer-journal/src/app/pages/home/home.component.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Home Page.
|
||||||
|
*
|
||||||
|
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||||
|
* @version 3
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html'
|
||||||
|
})
|
||||||
|
export class HomeComponent {
|
||||||
|
constructor() { }
|
||||||
|
}
|
@ -2,12 +2,22 @@ import { CommonModule } from '@angular/common'
|
|||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar'
|
import { MatToolbarModule } from '@angular/material/toolbar'
|
||||||
|
|
||||||
|
import { NavigationComponent } from './ui/navigation/navigation.component'
|
||||||
import { TopHeaderComponent } from './ui/top-header/top-header.component'
|
import { TopHeaderComponent } from './ui/top-header/top-header.component'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* myPrayerJournal Shared Module.
|
||||||
|
*
|
||||||
|
* This module contains UI components designed to be used throughout the application.
|
||||||
|
*
|
||||||
|
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||||
|
* @version 3
|
||||||
|
*/
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [TopHeaderComponent],
|
declarations: [
|
||||||
|
NavigationComponent,
|
||||||
|
TopHeaderComponent
|
||||||
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
MatToolbarModule
|
MatToolbarModule
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<ng-container *ngIf="auth.loggedIn">
|
||||||
|
<a routerLink="/journal">Journal</a>
|
||||||
|
<a routerLink="/requests/active">Active</a>
|
||||||
|
<a *ngIf="hasSnoozed" routerLink="/requests/snoozed">Snoozed</a>
|
||||||
|
<a routerLink="/requests/answered">Answered</a>
|
||||||
|
<a href="/user/log-off" (click)="logOff($event)">Log Off</a>
|
||||||
|
</ng-container>
|
||||||
|
<a *ngIf="!auth.loggedIn" href="/user/log-on" (click)="logOn($event)">Log On</a>
|
||||||
|
<a href="https://docs.prayerjournal.me" target="_blank">Docs</a>
|
@ -0,0 +1,11 @@
|
|||||||
|
a,
|
||||||
|
a:link,
|
||||||
|
a:visited
|
||||||
|
color: white
|
||||||
|
font-size: .9rem
|
||||||
|
text-transform: uppercase
|
||||||
|
text-decoration: none
|
||||||
|
padding: 0 1rem
|
||||||
|
|
||||||
|
a:hover
|
||||||
|
border-bottom: solid 1px white
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NavigationComponent } from './navigation.component';
|
||||||
|
|
||||||
|
describe('NavigationComponent', () => {
|
||||||
|
let component: NavigationComponent;
|
||||||
|
let fixture: ComponentFixture<NavigationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ NavigationComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavigationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,41 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { AuthService } from 'src/app/auth.service'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* myPrayerJournal Navigation.
|
||||||
|
*
|
||||||
|
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||||
|
* @version 3
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navigation',
|
||||||
|
templateUrl: './navigation.component.html',
|
||||||
|
styleUrls: ['./navigation.component.sass']
|
||||||
|
})
|
||||||
|
export class NavigationComponent {
|
||||||
|
|
||||||
|
// TODO: get this from the store's state
|
||||||
|
hasSnoozed = false
|
||||||
|
|
||||||
|
constructor(public auth: AuthService) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the log on process.
|
||||||
|
*
|
||||||
|
* @param e The click event (used to stop the default action)
|
||||||
|
*/
|
||||||
|
logOn(e: Event) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.auth.login()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the user off.
|
||||||
|
*
|
||||||
|
* @param e The click event (used to stop the default action)
|
||||||
|
*/
|
||||||
|
logOff(e: Event) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.auth.logout()
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<span>Links go here</span>
|
<app-navigation></app-navigation>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* myPrayerJournal Top Header.
|
||||||
|
*
|
||||||
|
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||||
|
* @version 3
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-top-header',
|
selector: 'app-top-header',
|
||||||
templateUrl: './top-header.component.html',
|
templateUrl: './top-header.component.html',
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>MyPrayerJournal</title>
|
<title>myPrayerJournal</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
@ -1 +1,37 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
p
|
||||||
|
margin-bottom: 0
|
||||||
|
footer
|
||||||
|
border-top: solid 1px lightgray
|
||||||
|
margin: 1rem -1rem 0
|
||||||
|
padding: 0 1rem
|
||||||
|
footer p
|
||||||
|
margin: 0
|
||||||
|
.mpj-full-page-card
|
||||||
|
font-size: 1rem
|
||||||
|
line-height: 1.25rem
|
||||||
|
.mpj-main-content
|
||||||
|
max-width: 61rem
|
||||||
|
padding: 0 .5rem
|
||||||
|
margin: auto
|
||||||
|
.mpj-request-text
|
||||||
|
white-space: pre-line
|
||||||
|
p.mpj-request-text
|
||||||
|
margin-top: 0
|
||||||
|
.mpj-text-center
|
||||||
|
text-align: center
|
||||||
|
.mpj-text-nowrap
|
||||||
|
white-space: nowrap
|
||||||
|
.mpj-text-right
|
||||||
|
text-align: right
|
||||||
|
.mpj-muted-text
|
||||||
|
color: rgba(0, 0, 0, .6)
|
||||||
|
.mpj-valign-top
|
||||||
|
vertical-align: top
|
||||||
|
.mpj-narrow
|
||||||
|
max-width: 40rem
|
||||||
|
margin: auto
|
||||||
|
.mpj-skinny
|
||||||
|
max-width: 20rem
|
||||||
|
margin: auto
|
||||||
|
.mpj-full-width
|
||||||
|
width: 100%
|
||||||
|
Loading…
Reference in New Issue
Block a user