diff --git a/angular.json b/angular.json index 7a4f1ef7..be4c78d3 100644 --- a/angular.json +++ b/angular.json @@ -94,5 +94,8 @@ } } } + }, + "cli": { + "analytics": false } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 02972627..577898b0 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 { 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)], diff --git a/src/app/app.component.html b/src/app/app.component.html index 17aaa0c6..18932f72 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,4 @@ - +
- +
diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8207184c..db4e4e29 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,10 +4,16 @@ 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 + ], 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..da45acf6 --- /dev/null +++ b/src/app/contacts/add/add.component.html @@ -0,0 +1,31 @@ +

Create Contact

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
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..58432102 --- /dev/null +++ b/src/app/contacts/add/add.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, Form } from '@angular/forms'; +import { ContactsService } from 'src/app/services/contacts.service'; +import { Contact } from 'src/app/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({ + name: ['', Validators.required], + street: ['', Validators.required], + city: ['', Validators.required], + }); + } + + addContact(): void { + const newContact: Contact = { + id: 0, + name: this.contactForm.value.name, + 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..210ea8af --- /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 { EditComponent } from './edit/edit.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + declarations: [ListComponent, AddComponent, ViewComponent, EditComponent], + imports: [CommonModule, ReactiveFormsModule, RouterModule], + exports: [AddComponent, ListComponent, ViewComponent, EditComponent], +}) +export class ContactsModule { } 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..e8de411e --- /dev/null +++ b/src/app/contacts/edit/edit.component.html @@ -0,0 +1,35 @@ +@if (contact) { +

Edit Contact

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

Contact does not exist

+} 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..98e0b22c --- /dev/null +++ b/src/app/contacts/edit/edit.component.ts @@ -0,0 +1,59 @@ +import { Component } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { ContactsService } from 'src/app/services/contacts.service'; +import { Contact } from 'src/app/models/contact'; +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 readonly formBuilder: FormBuilder, + private readonly contactService: ContactsService, + private readonly router: Router, + private readonly route: ActivatedRoute + ) { + this.contactForm = this.formBuilder.group({ + name: ['', 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!; + console.log(this.contact) + if (this.contact !== null && this.contact !== undefined) { + this.contactForm.patchValue({ + name: this.contact.name, + street: this.contact.street, + city: this.contact.city, + }); + } + }); + } + + editContact(): void { + if (!this.contactId) return; + + const editContact: Contact = { + id: this.contactId, + name: this.contactForm.value.name, + street: this.contactForm.value.street, + city: this.contactForm.value.city, + }; + this.contactService.EditContact(editContact); + 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..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..a8b0ea60 --- /dev/null +++ b/src/app/contacts/list/list.component.html @@ -0,0 +1,10 @@ +

Contacts list

+
+ @for (contact of contacts; track contact.id ) { +
+ {{contact.name}} + View + Edit +
+ } +
\ No newline at end of file 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..df2f6a53 --- /dev/null +++ b/src/app/contacts/list/list.component.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { Component } from '@angular/core'; +import { Contact } from 'src/app/models/contact'; +import { ContactsService } from 'src/app/services/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..9bb724d7 --- /dev/null +++ b/src/app/contacts/view/view.component.html @@ -0,0 +1,6 @@ +@if (contact) { +

{{ contact.name }}

+

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

+} @else { +

Contact not found

+} \ No newline at end of file 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..d6693990 --- /dev/null +++ b/src/app/contacts/view/view.component.ts @@ -0,0 +1,27 @@ +import { Component } from '@angular/core'; +import { ContactsService } from 'src/app/services/contacts.service'; +import { ActivatedRoute } from '@angular/router'; +import { Contact } from 'src/app/models/contact'; + +@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 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..8de936f3 --- /dev/null +++ b/src/app/data/contacts.ts @@ -0,0 +1,16 @@ +import { Contact } from "../models/contact"; + +export const CONTACTS: Contact[] = [ + { + id: 1, + name: 'Alice Johnson', + street: '123 Main St', + city: 'Springfield' + }, + { + id: 2, + name: 'Bob Smith', + street: '456 Oak Avenue', + city: 'Rivertown' + } +]; \ No newline at end of file diff --git a/src/app/layout/menu/menu.component.html b/src/app/layout/menu/menu.component.html index 7c5ec7a2..7b8fbfe7 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..4b7b91f1 --- /dev/null +++ b/src/app/models/contact.ts @@ -0,0 +1,6 @@ +export interface Contact{ + id: number | null; + name: string; + street: string; + city: string; +} \ No newline at end of file diff --git a/src/app/services/contacts.service.spec.ts b/src/app/services/contacts.service.spec.ts new file mode 100644 index 00000000..b64b5962 --- /dev/null +++ b/src/app/services/contacts.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ContactsService } from './contacts.service'; + +describe('ContactsService', () => { + let service: ContactsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ContactsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/contacts.service.ts b/src/app/services/contacts.service.ts new file mode 100644 index 00000000..01e7b3ee --- /dev/null +++ b/src/app/services/contacts.service.ts @@ -0,0 +1,32 @@ +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; + private lastId: number = this.contacts.length + + public AddContact(contact: Contact): void { + this.lastId++; + contact.id = this.lastId; + this.contacts.push(contact); + } + + GetContactById(id: number): Observable { + const contact = this.contacts.find((c) => c.id === id); + return of(contact) + } + + public EditContact(editContact: Contact): void { + const contact = this.contacts.find((c) => c.id === editContact.id); + if (contact === undefined) return; + contact.name = editContact.name; + contact.street = editContact.street; + contact.city = editContact.city + } + +}