From 4c81a1cd4bfe83875c3065ff673e4bbce0e7c604 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 19:14:39 +0100 Subject: [PATCH 01/15] Fixed coffee svg on footer --- .../app/common/footer/footer.component.html | 5 ++++- .../app/common/footer/footer.component.scss | 2 +- .../common/footer/footer.component.spec.ts | 22 +++++++++++++++---- .../src/app/common/footer/footer.module.ts | 4 +--- .../src/app/common/header/header.module.ts | 4 +--- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/apps/fretonator-web/src/app/common/footer/footer.component.html b/apps/fretonator-web/src/app/common/footer/footer.component.html index f54404d..1d642e8 100644 --- a/apps/fretonator-web/src/app/common/footer/footer.component.html +++ b/apps/fretonator-web/src/app/common/footer/footer.component.html @@ -2,7 +2,6 @@ diff --git a/apps/fretonator-web/src/app/common/footer/footer.component.scss b/apps/fretonator-web/src/app/common/footer/footer.component.scss index 7357ca7..484c248 100644 --- a/apps/fretonator-web/src/app/common/footer/footer.component.scss +++ b/apps/fretonator-web/src/app/common/footer/footer.component.scss @@ -113,7 +113,7 @@ } .footer__copyright { - color: var(--grey); + color: var(--offwhite); font-size: pxToRem(12); letter-spacing: var(--letter-spacing-display); font-weight: var(--font-weight-bold); diff --git a/apps/fretonator-web/src/app/common/footer/footer.component.spec.ts b/apps/fretonator-web/src/app/common/footer/footer.component.spec.ts index 163f646..4fdaec6 100644 --- a/apps/fretonator-web/src/app/common/footer/footer.component.spec.ts +++ b/apps/fretonator-web/src/app/common/footer/footer.component.spec.ts @@ -1,19 +1,33 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { FooterComponent } from './footer.component'; +import { Component } from '@angular/core'; +import { RouterTestingModule } from '@angular/router/testing'; +import { HeaderModule } from '../header/header.module'; +import { FooterModule } from './footer.module'; describe('FooterComponent', () => { - let component: FooterComponent; - let fixture: ComponentFixture; + @Component({ + selector: 'app-footer-spec', + template: ` + + ` + }) + class FooterComponentSpec { + } + + let component: FooterComponentSpec; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [FooterComponent], + declarations: [FooterComponentSpec], + imports: [FooterModule] }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(FooterComponent); + fixture = TestBed.createComponent(FooterComponentSpec); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/fretonator-web/src/app/common/footer/footer.module.ts b/apps/fretonator-web/src/app/common/footer/footer.module.ts index d7c7073..72993ea 100644 --- a/apps/fretonator-web/src/app/common/footer/footer.module.ts +++ b/apps/fretonator-web/src/app/common/footer/footer.module.ts @@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common'; import { FooterComponent } from './footer.component'; import { RouterModule } from '@angular/router'; import { CoffeeModule } from '../svgs/coffee/coffee.module'; -import { CoffeeComponent } from '../svgs/coffee/coffee.component'; @NgModule({ declarations: [FooterComponent], @@ -13,8 +12,7 @@ import { CoffeeComponent } from '../svgs/coffee/coffee.component'; CoffeeModule, ], exports: [ - FooterComponent, - CoffeeComponent + FooterComponent ] }) export class FooterModule { diff --git a/apps/fretonator-web/src/app/common/header/header.module.ts b/apps/fretonator-web/src/app/common/header/header.module.ts index e250996..d4beac4 100644 --- a/apps/fretonator-web/src/app/common/header/header.module.ts +++ b/apps/fretonator-web/src/app/common/header/header.module.ts @@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common'; import { HeaderComponent } from './header.component'; import { RouterModule } from '@angular/router'; import { LogoWithTextModule } from '../svgs/logo-with-text/logo-with-text.module'; -import { LogoWithTextComponent } from '../svgs/logo-with-text/logo-with-text.component'; import { LogoNoTextModule } from '../svgs/logo-no-text/logo-no-text.module'; @NgModule({ @@ -15,8 +14,7 @@ import { LogoNoTextModule } from '../svgs/logo-no-text/logo-no-text.module'; LogoNoTextModule ], exports: [ - HeaderComponent, - LogoWithTextComponent + HeaderComponent ] }) export class HeaderModule { From 0010b9a79124bfcc62cde5e0bad8064b9ee60b48 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 19:39:33 +0100 Subject: [PATCH 02/15] created getStandardModesInOrder to clean up getSimilarModes --- .../common/fret-map/fret-map.service.spec.ts | 23 +++++++++++++++++++ .../app/common/fret-map/fret-map.service.ts | 14 ++++++++++- apps/fretonator-web/src/app/util/constants.ts | 7 ------ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/apps/fretonator-web/src/app/common/fret-map/fret-map.service.spec.ts b/apps/fretonator-web/src/app/common/fret-map/fret-map.service.spec.ts index 1a9e9ac..880408f 100644 --- a/apps/fretonator-web/src/app/common/fret-map/fret-map.service.spec.ts +++ b/apps/fretonator-web/src/app/common/fret-map/fret-map.service.spec.ts @@ -1398,6 +1398,29 @@ describe('FretMapService:getNoteExtenderStringFromNoteObject', () => { }); }); +describe('FretMapService:getStandardModesInOrder', () => { + let service: FretMapService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FretMapService); + }); + + it('returns a set of modes correctly for starting mode dorian', () => { + const result = service.getStandardModesInOrder(1); + expect(result).toEqual([ + Mode.dorian, + Mode.phrygian, + Mode.lydian, + Mode.mixolydian, + Mode.aolian, + Mode.locrian, + Mode.ionian, + ]) + }) +}); + + describe('FretMapService:getSimilarModes', () => { let service: FretMapService; diff --git a/apps/fretonator-web/src/app/common/fret-map/fret-map.service.ts b/apps/fretonator-web/src/app/common/fret-map/fret-map.service.ts index 14c38a8..0d9427f 100644 --- a/apps/fretonator-web/src/app/common/fret-map/fret-map.service.ts +++ b/apps/fretonator-web/src/app/common/fret-map/fret-map.service.ts @@ -426,6 +426,16 @@ export class FretMapService { } }; + getStandardModesInOrder = (startModeIndex: number): Mode[] => { + const before = [...StandardModePatterns]; + const after = before.splice(startModeIndex); + + return [ + ...after, + ...before + ]; + }; + getSimilarModes = (modeMap: ModeMap, inputMode: Mode): SimilarModes => { const firstModeInPattern = StandardModePatterns.indexOf(inputMode); @@ -433,12 +443,14 @@ export class FretMapService { return []; } + const nextModes = this.getStandardModesInOrder(firstModeInPattern); + const similarModes = modeMap .map((noteObject, index) => ( { noteDisplayName: noteObject.displayName, note: noteObject.name, - mode: StandardModePatterns[firstModeInPattern + index], + mode: nextModes[index], noteExtender: this.getNoteExtenderStringFromNoteObject(noteObject) } )); diff --git a/apps/fretonator-web/src/app/util/constants.ts b/apps/fretonator-web/src/app/util/constants.ts index acbb382..3ad6b5b 100644 --- a/apps/fretonator-web/src/app/util/constants.ts +++ b/apps/fretonator-web/src/app/util/constants.ts @@ -628,13 +628,6 @@ export const StringFrequencies = { } export const StandardModePatterns = [ - Mode.ionian, - Mode.dorian, - Mode.phrygian, - Mode.lydian, - Mode.mixolydian, - Mode.aolian, - Mode.locrian, Mode.ionian, Mode.dorian, Mode.phrygian, From 716a355c87b1417a4be48725eb64e53ab9d2de92 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 19:56:14 +0100 Subject: [PATCH 03/15] Only show GA code on prod mode --- angular.json | 5 ++-- apps/fretonator-web/src/index-dev.html | 39 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 apps/fretonator-web/src/index-dev.html diff --git a/angular.json b/angular.json index f6b3487..adc8af8 100644 --- a/angular.json +++ b/angular.json @@ -16,7 +16,7 @@ "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/fretonator-web/browser", - "index": "apps/fretonator-web/src/index.html", + "index": "apps/fretonator-web/src/index-dev.html", "main": "apps/fretonator-web/src/main.ts", "polyfills": "apps/fretonator-web/src/polyfills.ts", "tsConfig": "apps/fretonator-web/tsconfig.app.json", @@ -44,6 +44,7 @@ "with": "apps/fretonator-web/src/environments/environment.prod.ts" } ], + "index": "apps/fretonator-web/src/index.html", "optimization": true, "outputHashing": "all", "sourceMap": false, @@ -204,4 +205,4 @@ } }, "defaultProject": "fretonator-web" -} \ No newline at end of file +} diff --git a/apps/fretonator-web/src/index-dev.html b/apps/fretonator-web/src/index-dev.html new file mode 100644 index 0000000..ed3daa7 --- /dev/null +++ b/apps/fretonator-web/src/index-dev.html @@ -0,0 +1,39 @@ + + + + + + + Fretonator - the ultimate interactive free guitar theory tool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From be067a3a976cb28b470e5e22a44de07260967c16 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 20:45:18 +0100 Subject: [PATCH 04/15] Scroll to top of mode selector when clicking on a similar mode chip --- .../app/common/chips/chip/chip.component.html | 1 + .../app/common/chips/chip/chip.component.ts | 7 +++++- .../fretonator/fretonator.component.html | 1 - .../common/fretonator/fretonator.component.ts | 1 - .../similar-modes.component.html | 5 ++-- .../similar-modes/similar-modes.component.ts | 9 ++++++- .../src/app/global.service.spec.ts | 16 +++++++++++++ apps/fretonator-web/src/app/global.service.ts | 17 +++++++++++++ .../home-index.component.spec.ts.snap | 24 +++++-------------- .../home/home-index/home-index.component.html | 2 +- .../home/home-index/home-index.component.ts | 15 ++++++++---- 11 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 apps/fretonator-web/src/app/global.service.spec.ts create mode 100644 apps/fretonator-web/src/app/global.service.ts diff --git a/apps/fretonator-web/src/app/common/chips/chip/chip.component.html b/apps/fretonator-web/src/app/common/chips/chip/chip.component.html index 786bef6..31dda1b 100644 --- a/apps/fretonator-web/src/app/common/chips/chip/chip.component.html +++ b/apps/fretonator-web/src/app/common/chips/chip/chip.component.html @@ -1,6 +1,7 @@ (); SelectedColor = SelectedColor; selected = false; @@ -48,4 +49,8 @@ export class ChipComponent implements DoCheck { return true; } + + onClick(event: MouseEvent) { + this.chipClick.emit(event); + } } diff --git a/apps/fretonator-web/src/app/common/fretonator/fretonator.component.html b/apps/fretonator-web/src/app/common/fretonator/fretonator.component.html index df0802d..8792292 100644 --- a/apps/fretonator-web/src/app/common/fretonator/fretonator.component.html +++ b/apps/fretonator-web/src/app/common/fretonator/fretonator.component.html @@ -89,7 +89,6 @@

- diff --git a/apps/fretonator-web/src/app/common/fretonator/fretonator.component.ts b/apps/fretonator-web/src/app/common/fretonator/fretonator.component.ts index 10a0e23..92504b2 100644 --- a/apps/fretonator-web/src/app/common/fretonator/fretonator.component.ts +++ b/apps/fretonator-web/src/app/common/fretonator/fretonator.component.ts @@ -33,7 +33,6 @@ export class FretonatorComponent { fretMode = FretMode.twelve; frets = FretReturner[this.fretMode]; - get fretModes() { return FretMode; } diff --git a/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.html b/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.html index a9f123f..e7b4d18 100644 --- a/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.html +++ b/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.html @@ -9,12 +9,13 @@

Did you know?

[note]="mode.note" [noteExtender]="mode.noteExtender" [mode]="mode.mode" - selectedColor="default">{{mode.noteDisplayName}} {{mode.mode | titlecase}} + (chipClick)="onModeChange()">{{mode.noteDisplayName}} {{mode.mode | titlecase}}

There are only 7 patterns you need to learn. Once you've memorised those patterns, - you can play any of the 7 standard modes (Ionian, Dorian, Phrygian, Lydian, Mixolydian, Aolian, Locrian) from + you can play any of the 7 standard modes (Ionian, Dorian, Phrygian, Lydian, Mixolydian, Aolian, Locrian) + from any starting note, anywhere on the fretboard.

These patterns always appear in the same order, one after the other.

diff --git a/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.ts b/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.ts index 412a827..8fe3443 100644 --- a/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.ts +++ b/apps/fretonator-web/src/app/common/fretonator/similar-modes/similar-modes.component.ts @@ -1,5 +1,6 @@ -import { Component, Input } from '@angular/core'; +import { Component, ElementRef, Input } from '@angular/core'; import { SimilarModes } from '../../../util/types'; +import { GlobalService } from '../../../global.service'; @Component({ selector: 'app-similar-modes', @@ -10,4 +11,10 @@ export class SimilarModesComponent { @Input() similarModes: SimilarModes; @Input() modeDisplayString: string; @Input() isTheoretical: boolean; + + constructor(private globalService: GlobalService){} + + onModeChange() { + this.globalService.getScrollTarget().scrollIntoView(); + } } diff --git a/apps/fretonator-web/src/app/global.service.spec.ts b/apps/fretonator-web/src/app/global.service.spec.ts new file mode 100644 index 0000000..0b3c4a7 --- /dev/null +++ b/apps/fretonator-web/src/app/global.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { GlobalService } from './global.service'; + +describe('GlobalService', () => { + let service: GlobalService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(GlobalService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/fretonator-web/src/app/global.service.ts b/apps/fretonator-web/src/app/global.service.ts new file mode 100644 index 0000000..616cd21 --- /dev/null +++ b/apps/fretonator-web/src/app/global.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class GlobalService { + + private scrollTarget: HTMLElement; + + getScrollTarget(): HTMLElement { + return this.scrollTarget; + } + + setScrollTarget(el: HTMLElement) { + this.scrollTarget = el; + } +} diff --git a/apps/fretonator-web/src/app/pages/home/home-index/__snapshots__/home-index.component.spec.ts.snap b/apps/fretonator-web/src/app/pages/home/home-index/__snapshots__/home-index.component.spec.ts.snap index d31d005..f568b25 100644 --- a/apps/fretonator-web/src/app/pages/home/home-index/__snapshots__/home-index.component.spec.ts.snap +++ b/apps/fretonator-web/src/app/pages/home/home-index/__snapshots__/home-index.component.spec.ts.snap @@ -1417,9 +1417,7 @@ exports[`HomeIndexComponent should create 1`] = ` class="chips" > - +
- + - + - + - + - +
-

Starting note

+

Starting note

; note: NoteSymbol = NoteSymbol.c; noteExtender: NoteExtenderSymbol; noteExtenderString: NoteExtenderString; @@ -28,7 +30,8 @@ export class HomeIndexComponent implements OnInit { private localStorage: AbstractDataService, private activatedRoute: ActivatedRoute, private fretMapService: FretMapService, - private metaService: MetaService + private metaService: MetaService, + private globalService: GlobalService ) { } @@ -50,6 +53,10 @@ export class HomeIndexComponent implements OnInit { } } + ngAfterViewInit(): void { + this.globalService.setScrollTarget(this.scrollTarget.nativeElement); + } + onRouteChange() { const routeData = this.activatedRoute.snapshot.data.selected; @@ -89,7 +96,7 @@ export class HomeIndexComponent implements OnInit { name: 'description', content: this.metaService.generateHomePageMetaDescription(this.note, this.noteExtenderString, this.mode) }); - + this.meta.updateTag({ name: 'twitter:description', content: this.metaService.generateHomePageMetaDescription(this.note, this.noteExtenderString, this.mode) From 0ddf20e16ec41cb4b76cacd52b50f26460f62200 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 22:15:09 +0100 Subject: [PATCH 05/15] Form set up --- .../src/app/app-routing.module.ts | 7 +- .../app/common/header/header.component.html | 9 +- .../contact-index.component.html | 54 +++++++++++ .../contact-index.component.scss | 5 + .../contact-index.component.spec.ts | 25 +++++ .../contact-index/contact-index.component.ts | 94 +++++++++++++++++++ .../pages/contact/contact-routing.module.ts | 23 +++++ .../contact-success.component.html | 1 + .../contact-success.component.scss | 0 .../contact-success.component.spec.ts | 25 +++++ .../contact-success.component.ts | 15 +++ .../src/app/pages/contact/contact.module.ts | 25 +++++ .../app/pages/contact/form-service.spec.ts | 16 ++++ .../src/app/pages/contact/form.service.ts | 31 ++++++ tools/schematics/generate-routes/index.ts | 2 + 15 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.scss create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.spec.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-routing.module.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.html create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.scss create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.spec.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/contact.module.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/form-service.spec.ts create mode 100644 apps/fretonator-web/src/app/pages/contact/form.service.ts diff --git a/apps/fretonator-web/src/app/app-routing.module.ts b/apps/fretonator-web/src/app/app-routing.module.ts index bffe656..e0fedb2 100644 --- a/apps/fretonator-web/src/app/app-routing.module.ts +++ b/apps/fretonator-web/src/app/app-routing.module.ts @@ -11,12 +11,17 @@ const about: Route = { loadChildren: () => import('./pages/about/about.module').then((mod) => mod.AboutModule), }; +const contact: Route = { + path: 'contact', + loadChildren: () => import('./pages/contact/contact.module').then((mod) => mod.ContactModule), +}; + const notFound: Route = { path: '**', loadChildren: () => import('./pages/not-found/not-found.module').then((mod) => mod.NotFoundModule), }; -const routes: Routes = [home, about, notFound]; +const routes: Routes = [home, about, contact, notFound]; @NgModule({ imports: [ diff --git a/apps/fretonator-web/src/app/common/header/header.component.html b/apps/fretonator-web/src/app/common/header/header.component.html index 750f6b1..3e3b921 100644 --- a/apps/fretonator-web/src/app/common/header/header.component.html +++ b/apps/fretonator-web/src/app/common/header/header.component.html @@ -46,11 +46,18 @@ >About
+
  • + Contact +
  • Support + target="_blank">Donate
  • diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html new file mode 100644 index 0000000..8ad829a --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html @@ -0,0 +1,54 @@ +
    +
    + + + +

    Enter your name, yo!

    +

    Please enter a valid name!

    +

    Come on, your name isn't actually {{name.errors?.maxlength.actualLength}} + characters long!

    +
    {{name.errors | json}}
    +
    +
    +
    + + + +

    An email address isn't required, but if you'd like me to reply to your message, + please enter a valid email address!

    +
    {{email.errors | json}}
    +
    + +
    +
    + + + +

    Go on, give me some feedback!

    +

    Go on, give me some feedback!

    +
    {{message.errors | json}}
    +
    + +
    + + +
    {{honeypot.errors | json}}
    +
    + + +

    Oh no! Didn't work.

    +
    + +
    diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.scss b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.scss new file mode 100644 index 0000000..2b37fcb --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.scss @@ -0,0 +1,5 @@ +@import '../../../../styles/mixins'; + +.form__submit { + @include hard_button_base(); +} diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.spec.ts b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.spec.ts new file mode 100644 index 0000000..01f6039 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactIndexComponent } from './contact-index.component'; + +describe('ContactIndexComponent', () => { + let component: ContactIndexComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ContactIndexComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ContactIndexComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts new file mode 100644 index 0000000..2a51366 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts @@ -0,0 +1,94 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormService } from '../form.service'; +import { tap } from 'rxjs/operators'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-contact-index', + templateUrl: './contact-index.component.html', + styleUrls: ['./contact-index.component.scss'] +}) +export class ContactIndexComponent implements OnInit { + formName: 'contact'; + formSubmitError = false; + form = new FormGroup({ + honeypot: new FormControl('', [ + Validators.maxLength(0) + ]), + name: new FormControl('', [ + Validators.required, + Validators.maxLength(81), + Validators.pattern(/[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)/) + ]), + email: new FormControl('', [ + Validators.email + ]), + message: new FormControl('', [ + Validators.required, + Validators.minLength(30) + ]), + submit: new FormControl('Submit', []) + }, { + updateOn: 'blur' + }); + + constructor(private formService: FormService, private router: Router) { + } + + ngOnInit(): void { + } + + onSubmit() { + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + + this.formSubmitError = false; + this.submit.disable(); + + const form = { + Name: this.name.value, + Email: this.email.value, + Message: this.message.value, + 'form-name': this.formName, + 'bot-field': this.honeypot.value + }; + + this.formService.submit('/contact/success', form) + .pipe(tap(() => this.submit.enable())) + .subscribe( + () => this.onSuccess(), + (err) => this.onFail()); + } + + async onSuccess() { + await (this.router.navigate(['/', 'contact', 'success'])); + } + + onFail() { + this.formSubmitError = true; + } + + get name() { + return this.form.get('name'); + } + + get email() { + return this.form.get('email'); + } + + get message() { + return this.form.get('message'); + } + + get submit() { + return this.form.get('submit'); + } + + get honeypot() { + return this.form.get('honeypot'); + } + +} diff --git a/apps/fretonator-web/src/app/pages/contact/contact-routing.module.ts b/apps/fretonator-web/src/app/pages/contact/contact-routing.module.ts new file mode 100644 index 0000000..206fef0 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-routing.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { Route, RouterModule, Routes } from '@angular/router'; +import { ContactIndexComponent } from './contact-index/contact-index.component'; +import { ContactSuccessComponent } from './contact-success/contact-success.component'; + +const contact: Route = { + path: '', + pathMatch: 'full', + component: ContactIndexComponent, +}; + +const success: Route = { + path: 'success', + component: ContactSuccessComponent, +}; + +const routes: Routes = [contact, success]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ContactRoutingModule { } diff --git a/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.html b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.html new file mode 100644 index 0000000..bf9f835 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.html @@ -0,0 +1 @@ +

    contact-success works!

    diff --git a/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.scss b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.spec.ts b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.spec.ts new file mode 100644 index 0000000..f7e4ec9 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactSuccessComponent } from './contact-success.component'; + +describe('ContactSuccessComponent', () => { + let component: ContactSuccessComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ContactSuccessComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ContactSuccessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.ts b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.ts new file mode 100644 index 0000000..4072751 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact-success/contact-success.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-contact-success', + templateUrl: './contact-success.component.html', + styleUrls: ['./contact-success.component.scss'] +}) +export class ContactSuccessComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/apps/fretonator-web/src/app/pages/contact/contact.module.ts b/apps/fretonator-web/src/app/pages/contact/contact.module.ts new file mode 100644 index 0000000..654a219 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/contact.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +import { ContactRoutingModule } from './contact-routing.module'; +import { ContactIndexComponent } from './contact-index/contact-index.component'; +import { ContactSuccessComponent } from './contact-success/contact-success.component'; +import { HttpClientModule } from '@angular/common/http'; +import { FormService } from './form.service'; + + +@NgModule({ + declarations: [ContactIndexComponent, ContactSuccessComponent], + imports: [ + CommonModule, + ContactRoutingModule, + ReactiveFormsModule, + FormsModule, + HttpClientModule + ], + providers: [ + FormService + ] +}) +export class ContactModule { } diff --git a/apps/fretonator-web/src/app/pages/contact/form-service.spec.ts b/apps/fretonator-web/src/app/pages/contact/form-service.spec.ts new file mode 100644 index 0000000..7365947 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/form-service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { FormService } from './form.service'; + +describe('FormServiceService', () => { + let service: FormService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FormService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/fretonator-web/src/app/pages/contact/form.service.ts b/apps/fretonator-web/src/app/pages/contact/form.service.ts new file mode 100644 index 0000000..9c3b158 --- /dev/null +++ b/apps/fretonator-web/src/app/pages/contact/form.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { retry } from 'rxjs/operators'; + +@Injectable( +) +export class FormService { + + constructor(private httpClient: HttpClient) { + } + + submit(location, form) { + + const headers = new HttpHeaders() + .set('Content-Type', 'application/x-www-form-urlencoded'); + + const options = { + headers, + responseType: 'text' as 'text', + }; + + const formBody = new URLSearchParams(); + + Object.entries(form) + .forEach(([key, value]) => + formBody.append(key, value as string)); + + return this.httpClient.post(location, formBody.toString(), options) + .pipe(retry(2)); + } +} diff --git a/tools/schematics/generate-routes/index.ts b/tools/schematics/generate-routes/index.ts index 6ffa8da..2a22717 100644 --- a/tools/schematics/generate-routes/index.ts +++ b/tools/schematics/generate-routes/index.ts @@ -7,6 +7,8 @@ export default function(schema: any): Rule { const staticPages = [ '/', '/about', + '/contact', + '/contact/success', '/404' ]; From bb259cf2ab48d03901204f7236ce40887bfa39e3 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Wed, 17 Jun 2020 22:47:27 +0100 Subject: [PATCH 06/15] Fix contact form name, ignore .netlify --- .gitignore | 3 +++ .../app/pages/contact/contact-index/contact-index.component.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f3f9d4b..53ebdf3 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ Thumbs.db # Routes _routes.txt + +# Local Netlify folder +.netlify \ No newline at end of file diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts index 2a51366..00c045d 100644 --- a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.ts @@ -10,7 +10,7 @@ import { Router } from '@angular/router'; styleUrls: ['./contact-index.component.scss'] }) export class ContactIndexComponent implements OnInit { - formName: 'contact'; + formName = 'Contact'; formSubmitError = false; form = new FormGroup({ honeypot: new FormControl('', [ From 1bdb713d75248f859bfd11dad769d9fbb1c2db09 Mon Sep 17 00:00:00 2001 From: Salma Alam-Naylor Date: Thu, 18 Jun 2020 10:57:36 +0100 Subject: [PATCH 07/15] Styles for contact form --- .../app/common/footer/footer.component.html | 3 +- .../app/common/footer/footer.component.scss | 7 ++ .../common/footer/footer.component.spec.ts | 5 +- .../header.component.spec.ts.snap | 13 ++- .../app/common/svgs/tick/tick.component.html | 5 + .../app/common/svgs/tick/tick.component.scss | 5 + .../common/svgs/tick/tick.component.spec.ts | 25 +++++ .../app/common/svgs/tick/tick.component.ts | 9 ++ .../src/app/common/svgs/tick/tick.module.ts | 16 +++ .../contact-index.component.html | 103 +++++++++++------ .../contact-index.component.scss | 104 ++++++++++++++++++ .../contact-index.component.spec.ts | 29 ++++- .../src/app/pages/contact/contact.module.ts | 8 +- ...m-service.spec.ts => form.service.spec.ts} | 8 +- apps/fretonator-web/src/styles.scss | 12 ++ package.json | 1 + 16 files changed, 303 insertions(+), 50 deletions(-) create mode 100644 apps/fretonator-web/src/app/common/svgs/tick/tick.component.html create mode 100644 apps/fretonator-web/src/app/common/svgs/tick/tick.component.scss create mode 100644 apps/fretonator-web/src/app/common/svgs/tick/tick.component.spec.ts create mode 100644 apps/fretonator-web/src/app/common/svgs/tick/tick.component.ts create mode 100644 apps/fretonator-web/src/app/common/svgs/tick/tick.module.ts rename apps/fretonator-web/src/app/pages/contact/{form-service.spec.ts => form.service.spec.ts} (57%) diff --git a/apps/fretonator-web/src/app/common/footer/footer.component.html b/apps/fretonator-web/src/app/common/footer/footer.component.html index 1d642e8..81003c5 100644 --- a/apps/fretonator-web/src/app/common/footer/footer.component.html +++ b/apps/fretonator-web/src/app/common/footer/footer.component.html @@ -57,7 +57,8 @@ - + Submit your feedback { @Component({ @@ -22,7 +21,7 @@ describe('FooterComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [FooterComponentSpec], - imports: [FooterModule] + imports: [FooterModule, RouterTestingModule] }).compileComponents(); })); diff --git a/apps/fretonator-web/src/app/common/header/__snapshots__/header.component.spec.ts.snap b/apps/fretonator-web/src/app/common/header/__snapshots__/header.component.spec.ts.snap index 7ea74eb..51544f2 100644 --- a/apps/fretonator-web/src/app/common/header/__snapshots__/header.component.spec.ts.snap +++ b/apps/fretonator-web/src/app/common/header/__snapshots__/header.component.spec.ts.snap @@ -89,6 +89,17 @@ exports[`HeaderComponent should match snapshot 1`] = ` About +
  • + + Contact + +
  • @@ -98,7 +109,7 @@ exports[`HeaderComponent should match snapshot 1`] = ` rel="nofollow noopener noreferrer" target="_blank" > - Support + Donate
  • diff --git a/apps/fretonator-web/src/app/common/svgs/tick/tick.component.html b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.html new file mode 100644 index 0000000..a1c0e40 --- /dev/null +++ b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.html @@ -0,0 +1,5 @@ + diff --git a/apps/fretonator-web/src/app/common/svgs/tick/tick.component.scss b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.scss new file mode 100644 index 0000000..4c58380 --- /dev/null +++ b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.scss @@ -0,0 +1,5 @@ +.svg { + flex: 1; + height: 100%; + fill: currentColor; +} diff --git a/apps/fretonator-web/src/app/common/svgs/tick/tick.component.spec.ts b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.spec.ts new file mode 100644 index 0000000..29d4f02 --- /dev/null +++ b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TickComponent } from './tick.component'; + +describe('TickComponent', () => { + let component: TickComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TickComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TickComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/fretonator-web/src/app/common/svgs/tick/tick.component.ts b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.ts new file mode 100644 index 0000000..b367a26 --- /dev/null +++ b/apps/fretonator-web/src/app/common/svgs/tick/tick.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-tick-svg', + templateUrl: './tick.component.html', + styleUrls: ['./tick.component.scss'] +}) +export class TickComponent { +} diff --git a/apps/fretonator-web/src/app/common/svgs/tick/tick.module.ts b/apps/fretonator-web/src/app/common/svgs/tick/tick.module.ts new file mode 100644 index 0000000..452ea1a --- /dev/null +++ b/apps/fretonator-web/src/app/common/svgs/tick/tick.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TickComponent } from './tick.component'; + + + +@NgModule({ + declarations: [TickComponent], + exports: [ + TickComponent + ], + imports: [ + CommonModule + ] +}) +export class TickModule { } diff --git a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html index 8ad829a..8d97175 100644 --- a/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html +++ b/apps/fretonator-web/src/app/pages/contact/contact-index/contact-index.component.html @@ -1,54 +1,85 @@ -
    -
    -