Compare commits

...

10 commits

19 changed files with 248 additions and 10 deletions

View file

@ -1 +1,2 @@
<app-toast-container aria-live="polite" aria-atomic="true"/>
<router-outlet/>

View file

@ -1,9 +1,10 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {ToastContainerComponent} from '../components/toast-container/toast-container/toast-container.component';
@Component({
selector: 'app-root',
imports: [RouterOutlet],
imports: [RouterOutlet, ToastContainerComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})

View file

@ -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>

View file

@ -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();
});
});

View file

@ -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)
}
}

View file

@ -0,0 +1,7 @@
:host {
position: fixed;
top: 0;
right: 0;
margin: 0.5em;
z-index: 1200;
}

View file

@ -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>
}

View file

@ -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();
});
});

View file

@ -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);
}

View file

@ -1,9 +1,3 @@
export interface ShowsApiResponse {
status: string
shows: Show[]
totalShows: number
}
export interface Show {
_id: string
title: string

View 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
View file

@ -0,0 +1,6 @@
export interface Toast {
header?: string;
body: string;
delay?: number;
htmlClass?: string;
}

View file

@ -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>

View file

@ -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({
selector: 'app-shows',
@ -7,5 +14,31 @@ import { Component } from '@angular/core';
styleUrl: './shows.component.css'
})
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)
}
}

View file

@ -1,7 +1,7 @@
import {inject, Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {ShowsApiResponse} from './shows-api';
import {ShowsApiResponse} from '../../interfaces/shows-api-response';
@Injectable({
providedIn: 'root'
@ -19,4 +19,8 @@ export class ShowsApiService {
getShows(): Observable<ShowsApiResponse> {
return this.http.get<ShowsApiResponse>(this.showsEndpoint)
}
sendShow(newShow: {}) {
return this.http.post(this.showsEndpoint, newShow)
}
}

View 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();
});
});

View 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);
}
}