diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 02972627..e125fc22 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,7 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { ListComponent } from './contacts/list/list.component'; +import { ViewComponent } from './contacts/view/view.component'; +import { AddComponent } from './contacts/add/add.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/view/:id', component: ViewComponent }, + { path: 'contacts/edit/:id', component: EditComponent }, +]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8207184c..cb337abb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,10 +4,11 @@ import { BrowserModule } from '@angular/platform-browser'; 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, LayoutModule], bootstrap: [AppComponent], }) -export class AppModule {} +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..2bc9116b --- /dev/null +++ b/src/app/contacts/add/add.component.html @@ -0,0 +1,38 @@ +

Add Contact

+
+ +
+ + + +
+ + + +
+ + + +
+ + +
+
+ +
+
diff --git a/src/app/contacts/add/add.component.ts b/src/app/contacts/add/add.component.ts new file mode 100644 index 00000000..a93fcf88 --- /dev/null +++ b/src/app/contacts/add/add.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Contact } from 'src/app/models/Contact'; +import { ContactForm } from 'src/app/models/ContactForm'; +import { ContactsService } from '../contacts.service'; +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 contactsService: 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: this.contactsService.GetNextId(), + firstName: this.contactForm.value.firstName, + lastName: this.contactForm.value.lastName, + street: this.contactForm.value.street, + city: this.contactForm.value.city, + } + + this.contactsService.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..713f9ea7 --- /dev/null +++ b/src/app/contacts/contacts.module.ts @@ -0,0 +1,15 @@ +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 { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +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..c2ecc9bc --- /dev/null +++ b/src/app/contacts/contacts.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { Contact } from '../models/Contact'; +import { testContacts } from '../data/testContactData'; +import { Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ContactsService { + public contacts: Contact[] = testContacts; + + public AddContact(c: Contact): void { + this.contacts.push(c); + } + + public EditContact(c: Contact): boolean { + const iToReplace = this.contacts.findIndex((curr) => (curr.id === c.id)); + + if (iToReplace > -1) { + this.contacts[iToReplace] = c; + return true; + } else { + return false; + } + } + + public GetContactById(id: number): Observable { + const beer = this.contacts.find((c) => c.id === id); + return of(beer); + } + + public GetNextId(): number { + if (this.contacts.length === 0) { + return 1; + } + + return Math.max(...this.contacts.map(c => c.id)) + 1; + } + + // constructor() { } +} diff --git a/src/app/contacts/edit/edit.component.css b/src/app/contacts/edit/edit.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/contacts/edit/edit.component.html b/src/app/contacts/edit/edit.component.html new file mode 100644 index 00000000..e8f06aa1 --- /dev/null +++ b/src/app/contacts/edit/edit.component.html @@ -0,0 +1,67 @@ +@if (contact) { +
+

Edit Contact

+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ + +
+
+
+ +} @else { + +

Contact not found

+
+} diff --git a/src/app/contacts/edit/edit.component.ts b/src/app/contacts/edit/edit.component.ts new file mode 100644 index 00000000..ba7760aa --- /dev/null +++ b/src/app/contacts/edit/edit.component.ts @@ -0,0 +1,76 @@ +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Contact } from 'src/app/models/Contact'; +import { ContactForm } from 'src/app/models/ContactForm'; +import { ContactsService } from '../contacts.service'; +import { ActivatedRoute, Router } from '@angular/router'; + +@Component({ + selector: 'app-edit', + standalone: false, + templateUrl: './edit.component.html', + styleUrl: './edit.component.css' +}) +export class EditComponent { + contact: Contact | null = null; + contactId: number | null = null; + contactForm: FormGroup; + + constructor( + private route: ActivatedRoute, + private readonly formBuilder: FormBuilder, + private readonly contactsService: ContactsService, + private readonly router: Router + ) { + this.contactForm = this.formBuilder.group({ + firstName: [this.contact?.firstName], + lastName: [this.contact?.lastName], + street: [this.contact?.street], + city: [this.contact?.city], + }) + } + + ngOnInit(): void { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + + if (!this.contactId) { + alert("Contact does not exist!") + this.router.navigate(['/contacts']); + return; + } + + this.contactsService.GetContactById(this.contactId).subscribe(contact => { + this.contact = contact ?? null; + + if (this.contact) { + this.contactForm.patchValue({ + firstName: this.contact.firstName ?? '', + lastName: this.contact.lastName ?? '', + street: this.contact.street ?? '', + city: this.contact.city ?? '' + }); + } else { + alert("Contact does not exist!") + this.router.navigate(['/contacts']); + return; + } + }); + } + + editContact(): void { + const newContact: Contact = { + id: this.contactId!, + firstName: this.contactForm.value.firstName, + lastName: this.contactForm.value.lastName, + street: this.contactForm.value.street, + city: this.contactForm.value.city, + } + + const isUpdated: boolean = this.contactsService.EditContact(newContact); + if (!isUpdated) { + alert("Something went wrong when editing! Contact not updated!") + } + this.contactForm.reset() + 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..22d0c2fc --- /dev/null +++ b/src/app/contacts/list/list.component.css @@ -0,0 +1,13 @@ +:host { + display: flex; + box-sizing: border-box; + width: 40vw; + flex-direction: column; +} + +.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..f38965e6 --- /dev/null +++ b/src/app/contacts/list/list.component.html @@ -0,0 +1,19 @@ +

Contacts

+ +@if (contacts) { +
+
+ {{ c.firstName }} {{ c.lastName }} + + + + +
+
+} @else { +
No contacts yet
+} diff --git a/src/app/contacts/list/list.component.ts b/src/app/contacts/list/list.component.ts new file mode 100644 index 00000000..06ede3ba --- /dev/null +++ b/src/app/contacts/list/list.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Contact } from 'src/app/models/Contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-list', + standalone: false, + templateUrl: './list.component.html', + styleUrl: './list.component.css' +}) +export class ListComponent { + contacts: Contact[] = []; + + constructor(private readonly contactsService: ContactsService) { + this.contacts = this.contactsService.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..2c6f52cf --- /dev/null +++ b/src/app/contacts/view/view.component.html @@ -0,0 +1,11 @@ +@if (contact) { + +

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

+

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

+
+} +@else { + +

Contact not found

+
+} diff --git a/src/app/contacts/view/view.component.ts b/src/app/contacts/view/view.component.ts new file mode 100644 index 00000000..bb428fc7 --- /dev/null +++ b/src/app/contacts/view/view.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Contact } from 'src/app/models/Contact'; +import { ContactsService } from '../contacts.service'; + +@Component({ + selector: 'app-view', + standalone: false, + templateUrl: './view.component.html', + styleUrl: './view.component.css' +}) +export class ViewComponent { + contact: Contact | null = null; + contactId: number | null = null; + + constructor(private route: ActivatedRoute, private contactsService: ContactsService) { } + + ngOnInit(): void { + this.contactId = Number(this.route.snapshot.paramMap.get('id')); + this.contactsService.GetContactById(this.contactId).subscribe((c) => { + c != undefined + ? this.contact = c + : this.contact = null + }) + } +} diff --git a/src/app/data/testContactData.ts b/src/app/data/testContactData.ts new file mode 100644 index 00000000..ef9334ad --- /dev/null +++ b/src/app/data/testContactData.ts @@ -0,0 +1,82 @@ +import { Contact } from "../models/Contact"; + +export const errorContact: Contact = { + id: -1, + firstName: "Error", + lastName: "Error", + street: "Error", + city: "Error", +} + +export const testContacts: Contact[] = [ + { + id: 1, + firstName: "Alice", + lastName: "Johnson", + street: "123 Maple St", + city: "Springfield", + }, + { + id: 2, + firstName: "Bob", + lastName: "Smith", + street: "456 Oak Ave", + city: "Riverton", + }, + { + id: 3, + firstName: "Charlie", + lastName: "Brown", + street: "789 Pine Rd", + city: "Lakeside", + }, + { + id: 4, + firstName: "Diana", + lastName: "Miller", + street: "321 Birch Blvd", + city: "Hillview", + }, + { + id: 5, + firstName: "Ethan", + lastName: "Davis", + street: "654 Cedar Ln", + city: "Brookfield", + }, + { + id: 6, + firstName: "Fiona", + lastName: "Wilson", + street: "987 Walnut Dr", + city: "Fairview", + }, + { + id: 7, + firstName: "George", + lastName: "Anderson", + street: "159 Cherry Ct", + city: "Riverside", + }, + { + id: 8, + firstName: "Hannah", + lastName: "Thomas", + street: "753 Elm St", + city: "Greenville", + }, + { + id: 9, + firstName: "Ian", + lastName: "Martinez", + street: "852 Willow Way", + city: "Sunnyvale", + }, + { + id: 10, + firstName: "Julia", + lastName: "Clark", + street: "951 Poplar Pl", + city: "Meadowbrook", + }, +]; diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html index 7c5ec7a2..de504fe9 100644 --- a/src/app/layout/menu/menu.component.html +++ b/src/app/layout/menu/menu.component.html @@ -1,5 +1,5 @@

Menu

+
  • Show Contact
  • +
  • Add Contact
  • + \ No newline at end of file diff --git a/src/app/models/Contact.ts b/src/app/models/Contact.ts new file mode 100644 index 00000000..9a4610e5 --- /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; +} diff --git a/src/app/models/ContactForm.ts b/src/app/models/ContactForm.ts new file mode 100644 index 00000000..c29142f3 --- /dev/null +++ b/src/app/models/ContactForm.ts @@ -0,0 +1,6 @@ +export interface ContactForm { + firstName?: string; + lastName?: string; + street?: string; + city?: string; +} \ No newline at end of file