BETTERZON-42: Functionality and design of various components for the search product UC

This commit is contained in:
Patrick Müller 2020-12-09 11:00:49 +01:00
parent e83b2d7511
commit acdc9a4e92
24 changed files with 219 additions and 29 deletions

View File

@ -13,6 +13,8 @@ import {NgApexchartsModule} from 'ng-apexcharts';
import { ProductSearchPageComponent } from './pages/product-search-page/product-search-page.component'; import { ProductSearchPageComponent } from './pages/product-search-page/product-search-page.component';
import { HeaderComponent } from './components/header/header.component'; import { HeaderComponent } from './components/header/header.component';
import { NewestPricesListComponent } from './components/newest-prices-list/newest-prices-list.component'; import { NewestPricesListComponent } from './components/newest-prices-list/newest-prices-list.component';
import {FormsModule} from '@angular/forms';
import { PageNotFoundPageComponent } from './pages/page-not-found-page/page-not-found-page.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -24,13 +26,15 @@ import { NewestPricesListComponent } from './components/newest-prices-list/newes
ProductDetailsComponent, ProductDetailsComponent,
ProductSearchPageComponent, ProductSearchPageComponent,
HeaderComponent, HeaderComponent,
NewestPricesListComponent NewestPricesListComponent,
PageNotFoundPageComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRouting, AppRouting,
HttpClientModule, HttpClientModule,
NgApexchartsModule NgApexchartsModule,
FormsModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -6,11 +6,13 @@ import {ProductListComponent} from './components/product-list/product-list.compo
import {LandingpageComponent} from './pages/landingpage/landingpage.component'; import {LandingpageComponent} from './pages/landingpage/landingpage.component';
import {ProductDetailPageComponent} from './pages/product-detail-page/product-detail-page.component'; import {ProductDetailPageComponent} from './pages/product-detail-page/product-detail-page.component';
import {ProductSearchPageComponent} from './pages/product-search-page/product-search-page.component'; import {ProductSearchPageComponent} from './pages/product-search-page/product-search-page.component';
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
const routes: Routes = [ const routes: Routes = [
{path: '', component: LandingpageComponent}, {path: '', component: LandingpageComponent},
{path: 'search', component: ProductSearchPageComponent}, {path: 'search', component: ProductSearchPageComponent},
{path: 'product/:id', component: ProductDetailPageComponent} {path: 'product/:id', component: ProductDetailPageComponent},
{path: '**', component: PageNotFoundPageComponent}
]; ];
@NgModule({ @NgModule({

View File

@ -0,0 +1,42 @@
.header {
width: auto;
background-color: dimgrey;
color: white;
text-align: center;
padding: .25em;
}
#headerContent {
display: flex;
}
.logo {
position: relative;
margin-left: 50%;
}
.searchBox {
position: relative;
margin: auto;
margin-left: 25%;
}
.searchBox input {
width: 100%;
padding: .25em;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.profileIcon {
position: relative;
margin: auto;
margin-left: 10%;
}
.icon-3d {
padding: 10px;
color: #fff;
}

View File

@ -1 +1,14 @@
<p>header works!</p> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<header class="header">
<div id="headerContent" class='wrap'>
<div class="logo">
<img src="assets/images/Betterzon.svg" alt="Betterzon Logo" width="50px" (click)="clickedLogo()">
</div>
<div class="searchBox">
<input type="text" [(ngModel)]="searchInput" placeholder="Search" (keyup.enter)="startedSearch()">
</div>
<div class="profileIcon">
Profile
</div>
</div>
</header>

View File

@ -1,4 +1,5 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
@ -6,10 +7,27 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./header.component.css'] styleUrls: ['./header.component.css']
}) })
export class HeaderComponent implements OnInit { export class HeaderComponent implements OnInit {
searchInput: string;
constructor() { } constructor(
private router: Router
) {
}
ngOnInit(): void { ngOnInit(): void {
} }
clickedLogo(): void {
this.router.navigate([('/')]);
}
startedSearch(): void {
this.redirectTo('/search', {queryParams: {q: this.searchInput}});
}
redirectTo(uri: string, queryParams: object): void {
this.router.navigateByUrl('/', {skipLocationChange: true}).then(() =>
this.router.navigate([uri], queryParams));
}
} }

View File

@ -0,0 +1,7 @@
.priceList {
max-width: 50%;
margin: auto;
margin: auto;
align-content: center;
text-align: center;
}

View File

@ -1 +1,3 @@
<p>newest-prices-list works!</p> <div class="priceList">
PriceList
</div>

View File

@ -4,11 +4,11 @@
margin: auto; margin: auto;
margin-bottom: .5em; margin-bottom: .5em;
display: grid; display: grid;
grid-template-columns: 20% 50% 30%; grid-template-columns: 20% 25% 25% 30%;
grid-template-areas: grid-template-areas:
'image title priceChart' 'image title title priceChart'
'image description priceChart' 'image description description priceChart'
'image priceAlarm bestPrice'; 'image priceAlarm bestPrice blank';
} }
/* Image div */ /* Image div */
@ -54,6 +54,7 @@
grid-area: priceAlarm; grid-area: priceAlarm;
border-style: solid; border-style: solid;
border-color: dimgrey; border-color: dimgrey;
border-radius: 5px;
padding: .25em; padding: .25em;
margin: auto; margin: auto;
} }
@ -63,6 +64,7 @@
grid-area: bestPrice; grid-area: bestPrice;
border-style: solid; border-style: solid;
border-color: dimgrey; border-color: dimgrey;
border-radius: 5px;
padding: .25em; padding: .25em;
margin: auto; margin: auto;
} }

View File

@ -1,4 +1,3 @@
<meta charset="UTF-8">
<div class="productItem"> <div class="productItem">
<div class="productImageContainer"> <div class="productImageContainer">
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/> <img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>

View File

@ -1,4 +1,6 @@
<meta charset="UTF-8"> <div *ngIf="products.length==0">
No Products found!
</div>
<div class="productItem" *ngFor="let product of products" (click)="clickedProduct(product)"> <div class="productItem" *ngFor="let product of products" (click)="clickedProduct(product)">
<div class="productImageContainer" *ngIf="showProductPicture===true"> <div class="productImageContainer" *ngIf="showProductPicture===true">
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/> <img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>

View File

@ -1,7 +1,7 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../services/api.service'; import {ApiService} from '../../services/api.service';
import {Product} from '../../models/product'; import {Product} from '../../models/product';
import {Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
@Component({ @Component({
selector: 'app-product-list', selector: 'app-product-list',
@ -9,33 +9,57 @@ import {Router} from '@angular/router';
styleUrls: ['./product-list.component.css'] styleUrls: ['./product-list.component.css']
}) })
export class ProductListComponent implements OnInit { export class ProductListComponent implements OnInit {
products: Product[]; products: Product[] = [];
@Input() numberOfProducts: number; @Input() numberOfProducts: number;
@Input() showProductPicture: boolean; @Input() showProductPicture: boolean;
type: string; @Input() searchQuery: string;
@Input() type: string;
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private router: Router private router: Router,
private route: ActivatedRoute
) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
this.getProducts(); this.loadParams();
}
loadParams(): void {
if (!this.numberOfProducts) { if (!this.numberOfProducts) {
this.numberOfProducts = 10; this.numberOfProducts = 10;
} }
if (!this.showProductPicture) { if (!this.showProductPicture) {
this.showProductPicture = false; this.showProductPicture = false;
} }
this.type = 'PLP'; if (!this.searchQuery) {
this.searchQuery = '';
}
if (!this.type) {
this.type = '';
}
switch (this.type) {
case 'search': {
this.getSearchedProducts();
break;
}
default: {
this.getProducts();
break;
}
}
} }
getProducts(): void { getProducts(): void {
this.apiService.getProducts().subscribe(products => this.products = products); this.apiService.getProducts().subscribe(products => this.products = products);
} }
getSearchedProducts(): void {
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => this.products = products);
}
clickedProduct(product: Product): void { clickedProduct(product: Product): void {
this.router.navigate([('/product/' + product.product_id)]); this.router.navigate([('/product/' + product.product_id)]);
} }

View File

@ -0,0 +1,5 @@
#mainComponents {
margin: 5em;
margin-top: .5em;
margin-bottom: .5em;
}

View File

@ -1,3 +1,5 @@
<app-header></app-header> <app-header></app-header>
<div id="mainComponents">
<app-product-list numberOfProducts="20" [showProductPicture]="true"></app-product-list> <app-product-list numberOfProducts="20" [showProductPicture]="true"></app-product-list>
</div>
<app-footer></app-footer> <app-footer></app-footer>

View File

@ -0,0 +1,2 @@
<h1>404</h1>
<p>Page not found!</p>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageNotFoundPageComponent } from './page-not-found-page.component';
describe('PageNotFoundPageComponent', () => {
let component: PageNotFoundPageComponent;
let fixture: ComponentFixture<PageNotFoundPageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageNotFoundPageComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageNotFoundPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-page-not-found-page',
templateUrl: './page-not-found-page.component.html',
styleUrls: ['./page-not-found-page.component.css']
})
export class PageNotFoundPageComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,3 @@
#mainComponents {
padding: 5em;
}

View File

@ -1,3 +1,6 @@
<app-header></app-header> <app-header></app-header>
<div id="mainComponents">
<app-product-details [productId]="productId"></app-product-details> <app-product-details [productId]="productId"></app-product-details>
<app-newest-prices-list></app-newest-prices-list>
</div>
<app-footer></app-footer> <app-footer></app-footer>

View File

@ -0,0 +1,5 @@
#mainComponents {
margin: 5em;
margin-top: .5em;
margin-bottom: .5em;
}

View File

@ -1,3 +1,6 @@
<app-header></app-header> <app-header></app-header>
<app-product-list numberOfProducts="20" [showProductPicture]="true"></app-product-list> <div id="mainComponents">
<app-product-list numberOfProducts="20" [showProductPicture]="true" searchQuery="{{searchTerm}}"
type="search"></app-product-list>
</div>
<app-footer></app-footer> <app-footer></app-footer>

View File

@ -19,17 +19,24 @@ export class ApiService {
getProduct(id): Observable<Product> { getProduct(id): Observable<Product> {
try { try {
const prod = this.http.get<Product>((this.apiUrl + '/products/' + id)); const prod = this.http.get<Product>((this.apiUrl + '/products/' + id));
console.log(prod);
return prod; return prod;
} catch (exception) { } catch (exception) {
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`); process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
} }
} }
getProductsByQuery(query): Observable<Product[]> {
try {
const prods = this.http.get<Product[]>((this.apiUrl + '/products/search/' + query));
return prods;
} catch (exception) {
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
}
}
getProducts(): Observable<Product[]> { getProducts(): Observable<Product[]> {
try { try {
const prods = this.http.get<Product[]>((this.apiUrl + '/products')); const prods = this.http.get<Product[]>((this.apiUrl + '/products'));
console.log(prods);
return prods; return prods;
} catch (exception) { } catch (exception) {
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`); process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1 +1,5 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
body {
margin: 0;
font-family: sans-serif;
}