diff --git a/src/app/address.ts b/src/app/address.ts new file mode 100644 index 0000000..f6936b0 --- /dev/null +++ b/src/app/address.ts @@ -0,0 +1,16 @@ +export class Address { + public lineOne: string; + public city: string; + public country: string; + public zipCode: number; + public state: string; + public lineTwo: string; + + constructor() { + this.lineOne = ''; + this.lineTwo = ''; + this.city = ''; + this.country = ''; + this.zipCode = null; + } +} diff --git a/src/app/api-contact.ts b/src/app/api-contact.ts new file mode 100644 index 0000000..8805cca --- /dev/null +++ b/src/app/api-contact.ts @@ -0,0 +1,22 @@ +import { Contact } from "./contact"; +import { Address } from "./address"; + +export class ApiContact { + + public id: string; + public firstName: string; + public lastName: string; + public address: Address; + public bio: string; + public gender?: string; + public email: string; + public phone?: string; + + + constructor(firstName: string, lastName: string, address: Address) { + this.firstName = firstName; + this.lastName = lastName; + this.address = address; + } + +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9a9dc69..8a34aef 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,12 +1,17 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http' + +import { ContactService } from './services/contact.service' import { AppComponent } from './app.component'; import { ContactFormComponent } from './components/contact-form/contact-form.component'; import { HeaderComponent } from './components/blocks/header/header.component'; import { ToDosComponent } from './components/blocks/to-dos/to-dos.component'; import { DocComponent } from './components/blocks/doc/doc.component'; +import { ContactAddressComponent } from './components/contact-address/contact-address.component'; +import { ApiContactPipe } from './pipes/api-contact.pipe'; @NgModule({ @@ -15,13 +20,16 @@ import { DocComponent } from './components/blocks/doc/doc.component'; ContactFormComponent, HeaderComponent, ToDosComponent, - DocComponent + DocComponent, + ContactAddressComponent, + ApiContactPipe ], imports: [ BrowserModule, - FormsModule + FormsModule, + HttpClientModule ], - providers: [], + providers: [ContactService, ApiContactPipe], bootstrap: [AppComponent] }) export class AppModule { } diff --git a/src/app/components/contact-address/contact-address.component.css b/src/app/components/contact-address/contact-address.component.css new file mode 100644 index 0000000..e0d207e --- /dev/null +++ b/src/app/components/contact-address/contact-address.component.css @@ -0,0 +1,28 @@ +.ng-valid[required], +.ng-valid.required { + border-left: 5px solid #42A948; + /* green */ +} + +.ng-invalid:not(form) { + border-left: 5px solid #a94442; + /* red */ +} + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + margin: 0; +} + +fieldset.pretty-set { + border: 1px solid lightgrey; + border-radius: 5px; + padding: 15px; +} + +fieldset.pretty-set > legend { + font-size: 1rem; +} diff --git a/src/app/components/contact-address/contact-address.component.html b/src/app/components/contact-address/contact-address.component.html new file mode 100644 index 0000000..1b16b2f --- /dev/null +++ b/src/app/components/contact-address/contact-address.component.html @@ -0,0 +1,45 @@ +
+ + + +
+
+ + +
+ This line is required!! +
+
+
+ + +
+
+
+
+ + +
+ This Country is required!! +
+
+
+ + +
+ This City is required!! +
+
+
+ + +
+
+ + +
+ This Zip Code is required!! +
+
+
+
diff --git a/src/app/components/contact-address/contact-address.component.spec.ts b/src/app/components/contact-address/contact-address.component.spec.ts new file mode 100644 index 0000000..c3518c7 --- /dev/null +++ b/src/app/components/contact-address/contact-address.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactAddressComponent } from './contact-address.component'; + +describe('ContactAddressComponent', () => { + let component: ContactAddressComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ContactAddressComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ContactAddressComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/contact-address/contact-address.component.ts b/src/app/components/contact-address/contact-address.component.ts new file mode 100644 index 0000000..038bec3 --- /dev/null +++ b/src/app/components/contact-address/contact-address.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Address } from '../../address'; + +@Component({ + selector: 'app-contact-address', + templateUrl: './contact-address.component.html', + styleUrls: ['./contact-address.component.css'] +}) +export class ContactAddressComponent implements OnInit { + + @Input() address: Address; + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/components/contact-form/contact-form.component.html b/src/app/components/contact-form/contact-form.component.html index 1fde111..15eb31d 100644 --- a/src/app/components/contact-form/contact-form.component.html +++ b/src/app/components/contact-form/contact-form.component.html @@ -10,29 +10,57 @@

+
-
+
First name is required!!
- +
- -
+ +
Last name is required!!
- +
+
+
+ + +
+ Must be a valid Email +
+
+
+ + +
+ Phone number is required!! +
+
+
+ + +
+
+
+ +
+ +
diff --git a/src/app/components/contact-form/contact-form.component.ts b/src/app/components/contact-form/contact-form.component.ts index b0c47dd..9dba3e4 100644 --- a/src/app/components/contact-form/contact-form.component.ts +++ b/src/app/components/contact-form/contact-form.component.ts @@ -1,6 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { Contact } from '../../contact'; +import { Address } from '../../address'; +import { ContactService } from '../../services/contact.service'; @Component({ selector: 'app-contact-form', @@ -10,17 +12,19 @@ import { Contact } from '../../contact'; export class ContactFormComponent implements OnInit { roles = ['Internal User', 'Admin', 'Customer']; - model = new Contact('Mohammed', '', 'Internal User'); + genders = ['male', 'female']; + model = new Contact('John', 'Doe', 'Internal User', new Address()); submitted: boolean = false; - constructor() { } + constructor(private contactService: ContactService) { } ngOnInit() { } onSubmit() { - this.submitted = true; + this.contactService.addContact(this.model) + .subscribe(() => this.submitted = true); } get diagnostic() { @@ -28,7 +32,6 @@ export class ContactFormComponent implements OnInit { } newContact() { - this.model = new Contact('', '', ''); + this.model = new Contact('', '', '', new Address()); } - } diff --git a/src/app/contact.ts b/src/app/contact.ts index 0c642c9..eb055dc 100644 --- a/src/app/contact.ts +++ b/src/app/contact.ts @@ -1,8 +1,25 @@ +import { Address } from "./address"; + export class Contact { - constructor( - public firstName: string, - public lastName: string, - public role: string, - public middleName?: string - ) { } + + public id: string; + public firstName: string; + public lastName: string; + public address: Address; + public role: string; + public middleName: string; + public gender: string; + public email: string; + public phone: string; + public bio: string; + public jobPosition: string; + public company: string; + + constructor(firstName: string, lastName: string,role: string, address: Address) { + this.firstName = firstName; + this.lastName = lastName; + this.role = role; + this.address = address; + this.gender = 'male'; + } } diff --git a/src/app/pipes/api-contact.pipe.spec.ts b/src/app/pipes/api-contact.pipe.spec.ts new file mode 100644 index 0000000..e4767aa --- /dev/null +++ b/src/app/pipes/api-contact.pipe.spec.ts @@ -0,0 +1,8 @@ +import { ApiContactPipe } from './api-contact.pipe'; + +describe('ApiContactPipe', () => { + it('create an instance', () => { + const pipe = new ApiContactPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/api-contact.pipe.ts b/src/app/pipes/api-contact.pipe.ts new file mode 100644 index 0000000..83845c6 --- /dev/null +++ b/src/app/pipes/api-contact.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Contact } from '../contact'; +import { ApiContact } from '../api-contact'; + +@Pipe({ + name: 'apiContact' +}) +export class ApiContactPipe implements PipeTransform { + + transform(value: Contact, args?: any): ApiContact { + const apiContact = new ApiContact(value.firstName, value.lastName, value.address); + apiContact.id = value.id; + apiContact.bio = value.bio; + apiContact.email = value.email; + apiContact.gender = value.gender; + apiContact.phone = value.phone; + return apiContact; + } + +} diff --git a/src/app/pipes/contact.pipe.spec.ts b/src/app/pipes/contact.pipe.spec.ts new file mode 100644 index 0000000..5b87386 --- /dev/null +++ b/src/app/pipes/contact.pipe.spec.ts @@ -0,0 +1,8 @@ +import { ContactPipe } from './contact.pipe'; + +describe('ContactPipe', () => { + it('create an instance', () => { + const pipe = new ContactPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/contact.pipe.ts b/src/app/pipes/contact.pipe.ts new file mode 100644 index 0000000..9f69f15 --- /dev/null +++ b/src/app/pipes/contact.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { ApiContact } from '../api-contact'; +import { Contact } from '../contact'; + +@Pipe({ + name: 'contact' +}) +export class ContactPipe implements PipeTransform { + + transform(value: ApiContact, args?: any): Contact { + const contact = new Contact(value.firstName, value.lastName, '', value.address); + contact.id = value.id; + contact.bio = value.bio; + contact.gender = value.gender; + contact.email = value.email; + contact.phone = value.phone; + return contact; + } + +} diff --git a/src/app/services/contact.service.spec.ts b/src/app/services/contact.service.spec.ts new file mode 100644 index 0000000..584e8a2 --- /dev/null +++ b/src/app/services/contact.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ContactService } from './contact.service'; + +describe('ContactService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ContactService] + }); + }); + + it('should be created', inject([ContactService], (service: ContactService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/services/contact.service.ts b/src/app/services/contact.service.ts new file mode 100644 index 0000000..2f3ed05 --- /dev/null +++ b/src/app/services/contact.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { catchError, map, tap } from 'rxjs/operators'; + +import { Contact } from '../contact' +import { Observable } from 'rxjs/Observable'; +import { of } from 'rxjs/observable/of'; +import { ApiContact } from '../api-contact'; + +import { ApiContactPipe } from '../pipes/api-contact.pipe' + +const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }) +}; + +@Injectable() +export class ContactService { + + private APIUrl = 'https://mini-crm-api.herokuapp.com/api/v1/contacts'; + + constructor( private http: HttpClient, private apiContactPipe: ApiContactPipe ) { + + } + + getContacts(): Observable { + return this.http.get(this.APIUrl).pipe( + catchError(this.handleError('getContacts', [])) + ); + } + + addContact (contact: Contact): Observable { + const apiContact = this.apiContactPipe.transform(contact); + return this.http.post(this.APIUrl, apiContact , httpOptions).pipe( + tap(_ => this.log(`updated hero id=${contact.firstName}`)), + catchError(this.handleError('updateHero')) + ); + } + + private log (message: string){ + console.log(message); + } + + private handleError (operation = 'operation', result?: T) { + return (error: any): Observable => { + + // TODO: send the error to remote logging infrastructure + // console.error(error); // log to console instead + + // TODO: better job of transforming error for user consumption + this.log(`${operation} failed: ${error.message}`); + + // Let the app keep running by returning an empty result. + return of(result as T); + }; + } + +}