Compare commits
10 commits
26d66397e4
...
93b82aedba
| Author | SHA1 | Date | |
|---|---|---|---|
| 93b82aedba | |||
| 35ce571079 | |||
| b92e54beab | |||
| 8fa6f48b65 | |||
| 0c0adb08ba | |||
| 68c7c8e1b8 | |||
| 7ee8e7e3ed | |||
| 22cc1dcc01 | |||
| 0a6fda1512 | |||
| 46b267d672 |
19 changed files with 248 additions and 10 deletions
|
|
@ -1 +1,2 @@
|
||||||
|
<app-toast-container aria-live="polite" aria-atomic="true"/>
|
||||||
<router-outlet/>
|
<router-outlet/>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import {ToastContainerComponent} from '../components/toast-container/toast-container/toast-container.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet],
|
imports: [RouterOutlet, ToastContainerComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.css'
|
styleUrl: './app.component.css'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title"> Add new show</h4>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" (click)="dismiss()"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form [formGroup]="newShowForm" (ngSubmit)="formSubmitted(newShowForm)">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Title</label>
|
||||||
|
<input formControlName="title" type="text" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Year</label>
|
||||||
|
<input formControlName="year" type="number" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Seasons</label>
|
||||||
|
<input formControlName="seasons" type="number" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Description</label>
|
||||||
|
<input formControlName="description" type="text" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid justify-content-md-end">
|
||||||
|
<button type="submit" class="btn btn-success" [disabled]="newShowForm.invalid">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CreateModalComponent } from './create-modal.component';
|
||||||
|
|
||||||
|
describe('CreateModalComponent', () => {
|
||||||
|
let component: CreateModalComponent;
|
||||||
|
let fixture: ComponentFixture<CreateModalComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [CreateModalComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CreateModalComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import {Component, inject} from '@angular/core';
|
||||||
|
import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create-modal',
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule
|
||||||
|
],
|
||||||
|
templateUrl: './create-modal.component.html',
|
||||||
|
styleUrl: './create-modal.component.css'
|
||||||
|
})
|
||||||
|
export class CreateModalComponent {
|
||||||
|
private activeModal = inject(NgbActiveModal)
|
||||||
|
protected newShowForm: FormGroup
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.newShowForm = new FormGroup({
|
||||||
|
title: new FormControl("", Validators.required),
|
||||||
|
year: new FormControl("", [Validators.required, Validators.min(1900)]),
|
||||||
|
seasons: new FormControl("", [Validators.required, Validators.min(1)]),
|
||||||
|
description: new FormControl("", Validators.required)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected dismiss() {
|
||||||
|
this.activeModal.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected formSubmitted(form: FormGroup) {
|
||||||
|
let show: {} = {
|
||||||
|
title: form.get("title")?.value,
|
||||||
|
year: form.get("year")?.value,
|
||||||
|
seasons: form.get("seasons")?.value,
|
||||||
|
description: form.get("description")?.value
|
||||||
|
}
|
||||||
|
console.log(show)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
:host {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0.5em;
|
||||||
|
z-index: 1200;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
@for (toast of toastService.toasts; track toast) {
|
||||||
|
<ngb-toast
|
||||||
|
[header]="toast.header || ''" [autohide]="true" [delay]="toast.delay || 5000"
|
||||||
|
(hidden)="toastService.remove(toast)"
|
||||||
|
[animation]="true" [class]="toast.htmlClass"
|
||||||
|
>{{ toast.body }}</ngb-toast>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ToastContainerComponent } from './toast-container.component';
|
||||||
|
|
||||||
|
describe('ToastContainerComponent', () => {
|
||||||
|
let component: ToastContainerComponent;
|
||||||
|
let fixture: ComponentFixture<ToastContainerComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ToastContainerComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ToastContainerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import {Component, inject} from '@angular/core';
|
||||||
|
import {ToastService} from '../../../services/toast/toast.service';
|
||||||
|
import {NgbToast} from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-toast-container',
|
||||||
|
imports: [
|
||||||
|
NgbToast
|
||||||
|
],
|
||||||
|
templateUrl: './toast-container.component.html',
|
||||||
|
styleUrl: './toast-container.component.css'
|
||||||
|
})
|
||||||
|
export class ToastContainerComponent {
|
||||||
|
toastService: ToastService = inject(ToastService);
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
export interface ShowsApiResponse {
|
|
||||||
status: string
|
|
||||||
shows: Show[]
|
|
||||||
totalShows: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Show {
|
export interface Show {
|
||||||
_id: string
|
_id: string
|
||||||
title: string
|
title: string
|
||||||
7
src/interfaces/shows-api-response.ts
Normal file
7
src/interfaces/shows-api-response.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {Show} from './show';
|
||||||
|
|
||||||
|
export interface ShowsApiResponse {
|
||||||
|
status: string
|
||||||
|
shows: Show[]
|
||||||
|
totalShows: number
|
||||||
|
}
|
||||||
6
src/interfaces/toast.ts
Normal file
6
src/interfaces/toast.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface Toast {
|
||||||
|
header?: string;
|
||||||
|
body: string;
|
||||||
|
delay?: number;
|
||||||
|
htmlClass?: string;
|
||||||
|
}
|
||||||
|
|
@ -1 +1,19 @@
|
||||||
<p>shows works!</p>
|
<div class="container">
|
||||||
|
<div class="row row-cols-4">
|
||||||
|
@for (show of shows; track show._id) {
|
||||||
|
<div class="card col">
|
||||||
|
<!-- <img src="">-->
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ show.title }}</h5>
|
||||||
|
<p class="card-text">{{ show.year }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-end mt-3">
|
||||||
|
<div class="col-2">
|
||||||
|
<button type="button" class="btn btn-primary" (click)="createNewShow()">Add new show</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,11 @@
|
||||||
import { Component } from '@angular/core';
|
import {Component, inject} from '@angular/core';
|
||||||
|
import {ShowsApiService} from '../../services/shows/shows-api.service';
|
||||||
|
import {Show} from '../../interfaces/show';
|
||||||
|
import {ShowsApiResponse} from '../../interfaces/shows-api-response';
|
||||||
|
import {Toast} from '../../interfaces/toast';
|
||||||
|
import {ToastService} from '../../services/toast/toast.service';
|
||||||
|
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import {CreateModalComponent} from '../../components/create-modal/create-modal/create-modal.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-shows',
|
selector: 'app-shows',
|
||||||
|
|
@ -7,5 +14,31 @@ import { Component } from '@angular/core';
|
||||||
styleUrl: './shows.component.css'
|
styleUrl: './shows.component.css'
|
||||||
})
|
})
|
||||||
export class ShowsComponent {
|
export class ShowsComponent {
|
||||||
|
private api: ShowsApiService = inject(ShowsApiService);
|
||||||
|
private toastService: ToastService = inject(ToastService);
|
||||||
|
private modalService: NgbModal = inject(NgbModal);
|
||||||
|
|
||||||
|
shows: Show[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let loadToast: Toast = {body: "Loading shows..."};
|
||||||
|
this.toastService.show(loadToast);
|
||||||
|
this.api.getShows().subscribe({
|
||||||
|
next: (response: ShowsApiResponse) => {
|
||||||
|
this.shows = response.shows;
|
||||||
|
}, error: (err: any) => {
|
||||||
|
console.error("Error: ", err);
|
||||||
|
}, complete: () => {
|
||||||
|
let successToast: Toast = {
|
||||||
|
body: "Shows have been loaded!",
|
||||||
|
htmlClass: "bg-success text-light"
|
||||||
|
}
|
||||||
|
this.toastService.show(successToast);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewShow() {
|
||||||
|
this.modalService.open(CreateModalComponent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {inject, Injectable} from '@angular/core';
|
import {inject, Injectable} from '@angular/core';
|
||||||
import {HttpClient} from '@angular/common/http';
|
import {HttpClient} from '@angular/common/http';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
import {ShowsApiResponse} from './shows-api';
|
import {ShowsApiResponse} from '../../interfaces/shows-api-response';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|
@ -19,4 +19,8 @@ export class ShowsApiService {
|
||||||
getShows(): Observable<ShowsApiResponse> {
|
getShows(): Observable<ShowsApiResponse> {
|
||||||
return this.http.get<ShowsApiResponse>(this.showsEndpoint)
|
return this.http.get<ShowsApiResponse>(this.showsEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendShow(newShow: {}) {
|
||||||
|
return this.http.post(this.showsEndpoint, newShow)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
16
src/services/toast/toast.service.spec.ts
Normal file
16
src/services/toast/toast.service.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ToastService } from './toast.service';
|
||||||
|
|
||||||
|
describe('ToastService', () => {
|
||||||
|
let service: ToastService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ToastService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
17
src/services/toast/toast.service.ts
Normal file
17
src/services/toast/toast.service.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {Toast} from '../../interfaces/toast';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ToastService {
|
||||||
|
toasts: Toast[] = [];
|
||||||
|
|
||||||
|
show(toast: Toast) {
|
||||||
|
this.toasts.push(toast);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(toast: Toast) {
|
||||||
|
this.toasts = this.toasts.filter(t => t != toast);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue