diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 02972627..8a755f82 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,10 +1,19 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { ListComponent } from './contacts/list/list.component'; +import { AddComponent } from './contacts/add/add.component'; +import { ViewComponent } from './contacts/view/view.component'; +import { EditComponent } from './contacts/edit/edit.component'; -const routes: Routes = []; +const routes: Routes = [ + { path: 'contacts', component: ListComponent }, + { path: 'contacts/add', component: AddComponent }, + { path: 'contacts/:id', component: ViewComponent }, + { path: 'contacts/edit/:id', component: EditComponent }, +]; @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class AppRoutingModule { } +export class AppRoutingModule {} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8207184c..82538837 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,9 +5,11 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { LayoutModule } from './layout/layout.module'; +import { ContactsModule } from './contacts/contacts.module'; + @NgModule({ declarations: [AppComponent], - imports: [BrowserModule, AppRoutingModule, LayoutModule], + imports: [BrowserModule, AppRoutingModule, LayoutModule, ContactsModule], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/contacts/add/add.component.css b/src/app/contacts/add/add.component.css new file mode 100644 index 00000000..04068da6 --- /dev/null +++ b/src/app/contacts/add/add.component.css @@ -0,0 +1,25 @@ +:host { + display: flex; + box-sizing: border-box; + width: 40vw; + flex-direction: column; +} + +form { + display: flex; + flex-direction: column; + flex: 1; + gap: 0.5em; +} + +label { + font-weight: bold; +} + +.actions { + display: flex; +} + +.spacer { + flex: 1; +} diff --git a/src/app/contacts/add/add.component.html b/src/app/contacts/add/add.component.html new file mode 100644 index 00000000..ede25f63 --- /dev/null +++ b/src/app/contacts/add/add.component.html @@ -0,0 +1,52 @@ +
+

Add Beer

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
diff --git a/src/app/contacts/add/add.component.spec.ts b/src/app/contacts/add/add.component.spec.ts new file mode 100644 index 00000000..f4528470 --- /dev/null +++ b/src/app/contacts/add/add.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddComponent } from './add.component'; + +describe('AddComponent', () => { + let component: AddComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AddComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AddComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/add/add.component.ts b/src/app/contacts/add/add.component.ts new file mode 100644 index 00000000..ca8425af --- /dev/null +++ b/src/app/contacts/add/add.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { ContactsService } from '../contacts.service'; +import { Contact } from '../../models/contact'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-add', + standalone: false, + templateUrl: './add.component.html', + styleUrl: './add.component.css', +}) +export class AddComponent { + contactForm: FormGroup; + constructor( + private readonly formBuilder: FormBuilder, + private readonly contactService: ContactsService, + private readonly router: Router + ) { + this.contactForm = this.formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required], + }); + } + addContact(): void { + const newContact: Contact = { + id: 0, + firstName: this.contactForm.value.firstName, + lastName: this.contactForm.value.lastName, + street: this.contactForm.value.street, + city: this.contactForm.value.city, + }; + this.contactService.AddContact(newContact); + this.contactForm.reset(); + this.router.navigate(['/contacts']); + } +} diff --git a/src/app/contacts/contacts.module.ts b/src/app/contacts/contacts.module.ts new file mode 100644 index 00000000..c6cdab31 --- /dev/null +++ b/src/app/contacts/contacts.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ListComponent } from './list/list.component'; +import { AddComponent } from './add/add.component'; +import { ViewComponent } from './view/view.component'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { EditComponent } from './edit/edit.component'; + +@NgModule({ + declarations: [ListComponent, AddComponent, ViewComponent, EditComponent], + imports: [CommonModule, ReactiveFormsModule, RouterModule], + exports: [AddComponent, ListComponent, ViewComponent, EditComponent], +}) +export class ContactsModule {} diff --git a/src/app/contacts/contacts.service.ts b/src/app/contacts/contacts.service.ts new file mode 100644 index 00000000..64c49055 --- /dev/null +++ b/src/app/contacts/contacts.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { Contact } from '../models/contact'; +import { CONTACTS } from '../data/contacts'; +import { Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class ContactsService { + public contacts: Contact[] = CONTACTS; + + public AddContact(contact: Contact): void { + const maxId = + this.contacts.length > 0 + ? Math.max(...this.contacts.map((c) => c.id)) + : 0; + + const newContact = { ...contact, id: maxId + 1 }; + this.contacts.push(newContact); + } + GetContactById(id: number): Observable { + const contact = this.contacts.find((c) => c.id === id); + return of(contact); + } + public UpdateContact(updatedContact: Contact): void { + const index = this.contacts.findIndex((c) => c.id === updatedContact.id); + if (index !== -1) { + this.contacts[index] = updatedContact; + } + } +} diff --git a/src/app/contacts/edit/edit.component.css b/src/app/contacts/edit/edit.component.css new file mode 100644 index 00000000..04068da6 --- /dev/null +++ b/src/app/contacts/edit/edit.component.css @@ -0,0 +1,25 @@ +:host { + display: flex; + box-sizing: border-box; + width: 40vw; + flex-direction: column; +} + +form { + display: flex; + flex-direction: column; + flex: 1; + gap: 0.5em; +} + +label { + font-weight: bold; +} + +.actions { + display: flex; +} + +.spacer { + flex: 1; +} diff --git a/src/app/contacts/edit/edit.component.html b/src/app/contacts/edit/edit.component.html new file mode 100644 index 00000000..e1ccabec --- /dev/null +++ b/src/app/contacts/edit/edit.component.html @@ -0,0 +1,62 @@ +
+

Edit Contact

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + + +

Contact not found

+
+
diff --git a/src/app/contacts/edit/edit.component.spec.ts b/src/app/contacts/edit/edit.component.spec.ts new file mode 100644 index 00000000..6676ccfd --- /dev/null +++ b/src/app/contacts/edit/edit.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditComponent } from './edit.component'; + +describe('EditComponent', () => { + let component: EditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [EditComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/edit/edit.component.ts b/src/app/contacts/edit/edit.component.ts new file mode 100644 index 00000000..81640970 --- /dev/null +++ b/src/app/contacts/edit/edit.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ContactsService } from '../contacts.service'; +import { Contact } from '../../models/contact'; + +@Component({ + selector: 'app-edit', + standalone: false, + templateUrl: './edit.component.html', + styleUrls: ['./edit.component.css'], +}) +export class EditComponent implements OnInit { + contactForm: FormGroup; + contactId!: number; + contact: Contact | null = null; + + constructor( + private readonly fb: FormBuilder, + private readonly route: ActivatedRoute, + private readonly router: Router, + private readonly contactService: ContactsService + ) { + this.contactForm = this.fb.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required], + }); + } + + ngOnInit(): void { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contactService.GetContactById(this.contactId).subscribe((data) => { + this.contact = data!; + }); + + if (this.contact) { + this.contactForm.patchValue({ + firstName: this.contact.firstName, + lastName: this.contact.lastName, + street: this.contact.street, + city: this.contact.city, + }); + } + } + + updateContact(): void { + const updatedContact: Contact = { + id: this.contactId, + ...this.contactForm.value, + }; + + this.contactService.UpdateContact(updatedContact); + this.router.navigate(['/contacts']); + } +} diff --git a/src/app/contacts/list/list.component.css b/src/app/contacts/list/list.component.css new file mode 100644 index 00000000..a7943571 --- /dev/null +++ b/src/app/contacts/list/list.component.css @@ -0,0 +1,11 @@ +:host { + padding: 0.5em; + width: 400px; +} + +.contact { + display: flex; + justify-content: space-between; + border-bottom: 1px solid #ddd; + padding: 0.5em 0; +} diff --git a/src/app/contacts/list/list.component.html b/src/app/contacts/list/list.component.html new file mode 100644 index 00000000..f02e64ef --- /dev/null +++ b/src/app/contacts/list/list.component.html @@ -0,0 +1,19 @@ +

Contacts

+ +
+
+
+
+ {{ contact.firstName }} {{ contact.lastName }} + + View + + + Edit + +
+
+
+
+ +
No contacts yet
diff --git a/src/app/contacts/list/list.component.spec.ts b/src/app/contacts/list/list.component.spec.ts new file mode 100644 index 00000000..b602c867 --- /dev/null +++ b/src/app/contacts/list/list.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ListComponent } from './list.component'; + +describe('ListComponent', () => { + let component: ListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ListComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/list/list.component.ts b/src/app/contacts/list/list.component.ts new file mode 100644 index 00000000..1352c52f --- /dev/null +++ b/src/app/contacts/list/list.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Contact } from '../../models/contact'; +import { ContactsService } from '../contacts.service'; +import { Injectable } from '@angular/core'; + +@Component({ + selector: 'app-list', + standalone: false, + templateUrl: './list.component.html', + styleUrl: './list.component.css', +}) +export class ListComponent { + contacts: Contact[] = []; + constructor(private readonly contactService: ContactsService) { + this.contacts = this.contactService.contacts; + } +} diff --git a/src/app/contacts/view/view.component.css b/src/app/contacts/view/view.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/contacts/view/view.component.html b/src/app/contacts/view/view.component.html new file mode 100644 index 00000000..2bdc652c --- /dev/null +++ b/src/app/contacts/view/view.component.html @@ -0,0 +1,10 @@ +
+

{{ contact.firstName }} {{ contact.lastName }}

+

{{ contact.street }}, {{ contact.city }}

+
+ + + +

Contact not found

+
+
diff --git a/src/app/contacts/view/view.component.spec.ts b/src/app/contacts/view/view.component.spec.ts new file mode 100644 index 00000000..380ab164 --- /dev/null +++ b/src/app/contacts/view/view.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewComponent } from './view.component'; + +describe('ViewComponent', () => { + let component: ViewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ViewComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contacts/view/view.component.ts b/src/app/contacts/view/view.component.ts new file mode 100644 index 00000000..dd6c5ce8 --- /dev/null +++ b/src/app/contacts/view/view.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { ContactsService } from '../contacts.service'; +import { ActivatedRoute } from '@angular/router'; +import { Contact } from '../../models/contact'; + +@Component({ + selector: 'app-view', + standalone: false, + templateUrl: './view.component.html', + styleUrl: './view.component.css', +}) +export class ViewComponent implements OnInit { + contact: Contact | null = null; + contactId: number | null = null; + + constructor( + private route: ActivatedRoute, + private contactService: ContactsService + ) {} + + ngOnInit(): void { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contactService.GetContactById(this.contactId).subscribe((data) => { + this.contact = data!; + }); + } +} diff --git a/src/app/data/contacts.ts b/src/app/data/contacts.ts new file mode 100644 index 00000000..45e36d17 --- /dev/null +++ b/src/app/data/contacts.ts @@ -0,0 +1,18 @@ +import { Contact } from '../models/contact'; + +export const CONTACTS: Contact[] = [ + { + id: 0, + firstName: 'Florian', + lastName: 'Berg', + street: 'Pavington 5', + city: 'Chelsea', + }, + { + id: 1, + firstName: 'Adam', + lastName: 'Mayer', + street: 'Langgata 12', + city: 'Sandnes', + }, +]; diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html index 7c5ec7a2..aed3259d 100644 --- a/src/app/layout/menu/menu.component.html +++ b/src/app/layout/menu/menu.component.html @@ -1,5 +1,5 @@

Menu

diff --git a/src/app/models/contact.ts b/src/app/models/contact.ts new file mode 100644 index 00000000..e4f470d2 --- /dev/null +++ b/src/app/models/contact.ts @@ -0,0 +1,7 @@ +export interface Contact { + id: number; + firstName: string; + lastName: string; + street: string; + city: string; +}