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

This commit is contained in:
Paddy committed 2020-12-09 11:00:49 +01:00
1 parent e83b2d7511
commit acdc9a4e92
24 files changed
+219 -29

No files matched your search

+6 -2
View File
@@ -13,6 +13,8 @@ import {NgApexchartsModule} from 'ng-apexcharts';
import { ProductSearchPageComponent } from './pages/product-search-page/product-search-page.component';
import { HeaderComponent } from './components/header/header.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({
declarations: [
@@ -24,13 +26,15 @@ import { NewestPricesListComponent } from './components/newest-prices-list/newes
ProductDetailsComponent,
ProductSearchPageComponent,
HeaderComponent,
NewestPricesListComponent
NewestPricesListComponent,
PageNotFoundPageComponent
],
imports: [
BrowserModule,
AppRouting,
HttpClientModule,
NgApexchartsModule
NgApexchartsModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
+3 -1
View File
@@ -6,11 +6,13 @@ import {ProductListComponent} from './components/product-list/product-list.compo
import {LandingpageComponent} from './pages/landingpage/landingpage.component';
import {ProductDetailPageComponent} from './pages/product-detail-page/product-detail-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 = [
{path: '', component: LandingpageComponent},
{path: 'search', component: ProductSearchPageComponent},
{path: 'product/:id', component: ProductDetailPageComponent}
{path: 'product/:id', component: ProductDetailPageComponent},
{path: '**', component: PageNotFoundPageComponent}
];
@NgModule({
@@ -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;
}
@@ -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>
@@ -1,15 +1,33 @@
import { Component, OnInit } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
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));
}
}
@@ -0,0 +1,7 @@
.priceList {
max-width: 50%;
margin: auto;
margin: auto;
align-content: center;
text-align: center;
}
@@ -1 +1,3 @@
<p>newest-prices-list works!</p>
<div class="priceList">
PriceList
</div>
@@ -4,11 +4,11 @@
margin: auto;
margin-bottom: .5em;
display: grid;
grid-template-columns: 20% 50% 30%;
grid-template-columns: 20% 25% 25% 30%;
grid-template-areas:
'image title priceChart'
'image description priceChart'
'image priceAlarm bestPrice';
'image title title priceChart'
'image description description priceChart'
'image priceAlarm bestPrice blank';
}
/* Image div */
@@ -54,6 +54,7 @@
grid-area: priceAlarm;
border-style: solid;
border-color: dimgrey;
border-radius: 5px;
padding: .25em;
margin: auto;
}
@@ -63,6 +64,7 @@
grid-area: bestPrice;
border-style: solid;
border-color: dimgrey;
border-radius: 5px;
padding: .25em;
margin: auto;
}
@@ -1,4 +1,3 @@
<meta charset="UTF-8">
<div class="productItem">
<div class="productImageContainer">
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>
@@ -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="productImageContainer" *ngIf="showProductPicture===true">
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>
@@ -1,7 +1,7 @@
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../services/api.service';
import {Product} from '../../models/product';
import {Router} from '@angular/router';
import {ActivatedRoute, Router} from '@angular/router';
@Component({
selector: 'app-product-list',
@@ -9,33 +9,57 @@ import {Router} from '@angular/router';
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
products: Product[];
products: Product[] = [];
@Input() numberOfProducts: number;
@Input() showProductPicture: boolean;
type: string;
@Input() searchQuery: string;
@Input() type: string;
constructor(
private apiService: ApiService,
private router: Router
private router: Router,
private route: ActivatedRoute
) {
}
ngOnInit(): void {
this.getProducts();
this.loadParams();
}
loadParams(): void {
if (!this.numberOfProducts) {
this.numberOfProducts = 10;
}
if (!this.showProductPicture) {
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 {
this.apiService.getProducts().subscribe(products => this.products = products);
}
getSearchedProducts(): void {
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => this.products = products);
}
clickedProduct(product: Product): void {
this.router.navigate([('/product/' + product.product_id)]);
}
@@ -0,0 +1,5 @@
#mainComponents {
margin: 5em;
margin-top: .5em;
margin-bottom: .5em;
}
@@ -1,3 +1,5 @@
<app-header></app-header>
<app-product-list numberOfProducts="20" [showProductPicture]="true"></app-product-list>
<div id="mainComponents">
<app-product-list numberOfProducts="20" [showProductPicture]="true"></app-product-list>
</div>
<app-footer></app-footer>
@@ -0,0 +1,2 @@
<h1>404</h1>
<p>Page not found!</p>
@@ -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();
});
});
@@ -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 {
}
}
@@ -0,0 +1,3 @@
#mainComponents {
padding: 5em;
}
@@ -1,3 +1,6 @@
<app-header></app-header>
<app-product-details [productId]="productId"></app-product-details>
<div id="mainComponents">
<app-product-details [productId]="productId"></app-product-details>
<app-newest-prices-list></app-newest-prices-list>
</div>
<app-footer></app-footer>
@@ -0,0 +1,5 @@
#mainComponents {
margin: 5em;
margin-top: .5em;
margin-bottom: .5em;
}
@@ -1,3 +1,6 @@
<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>
+9 -2
View File
@@ -19,17 +19,24 @@ export class ApiService {
getProduct(id): Observable<Product> {
try {
const prod = this.http.get<Product>((this.apiUrl + '/products/' + id));
console.log(prod);
return prod;
} catch (exception) {
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[]> {
try {
const prods = this.http.get<Product[]>((this.apiUrl + '/products'));
console.log(prods);
return prods;
} catch (exception) {
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 454.14 358"><defs><style>.cls-1{fill:#d63031;}.cls-2,.cls-8,.cls-9{fill:#fff;}.cls-3,.cls-7,.cls-8{opacity:0.2;}.cls-4{opacity:0.4;}.cls-5{fill:#e6e6e6;}.cls-6{fill:#f2f2f2;}.cls-9{opacity:0.3;}.cls-10{fill:#092ce3;}.cls-11{fill:#81ecec;}</style></defs><g id="Ebene_1" data-name="Ebene 1"><path class="cls-1" d="M642.6,590.49a178,178,0,0,1-140-69.45,178.5,178.5,0,0,1,140-287.53h2.31c.59,0,1.18,0,1.77,0h.37c1,0,2.07,0,3.1.08l1.22,0,1.91.09c1,0,2,.12,3.07.19a178.52,178.52,0,0,1,17,354.15l-.1,0,.13,0c-1.26.21-2.44.39-3.64.55s-2.42.34-3.62.49-2.43.3-3.65.42-2.45.24-3.68.34c-2.32.2-4.62.34-6.83.44l-.63,0-.49,0c-1,0-2,.07-3,.08h-.27l-.9,0c-.59,0-1.16,0-1.75,0Z" transform="translate(-403.93 -233)"/><path d="M644.94,234c.58,0,1.15,0,1.76,0H647c.92,0,1.94,0,3.1.08l.6,0,.59,0,.88,0,.9,0h.12c1,0,2.06.12,3.08.19a178,178,0,0,1,17,353.15l-.11,0c-1.06.18-2.21.35-3.53.54-1,.16-2.18.31-3.61.49-1.21.16-2.43.3-3.64.42s-2.29.22-3.66.33c-2.33.2-4.63.35-6.82.44h-.27l-.32,0-.56,0c-1.12,0-2.06.07-2.95.08h-1.17c-.59,0-1.17,0-1.76,0h-2.3a178,178,0,0,1,0-356h2.34m0-1H642.6a179,179,0,0,0,0,358h2.31c.6,0,1.19,0,1.79,0l.84,0h.31c1,0,2,0,3-.08l.55,0,.61,0q3.45-.15,6.85-.44c1.23-.1,2.46-.21,3.69-.34s2.44-.26,3.66-.42,2.43-.31,3.63-.49,2.39-.35,3.57-.55h.05a179,179,0,0,0-17.06-355.14c-1-.07-2.06-.14-3.1-.19h-.16l-1.74-.08-1.21,0c-1,0-2.08-.07-3.12-.08h-.35c-.6,0-1.19,0-1.79,0Z" transform="translate(-403.93 -233)"/><ellipse class="cls-2" cx="237.97" cy="181" rx="107.13" ry="106.5"/><path d="M641.91,308c58.79,0,106.62,47.55,106.62,106S700.7,520,641.91,520,535.28,472.45,535.28,414s47.83-106,106.63-106m0-1c-59.44,0-107.63,47.91-107.63,107s48.19,107,107.63,107,107.62-47.91,107.62-107S701.35,307,641.91,307Z" transform="translate(-403.93 -233)"/><g class="cls-3"><path class="cls-2" d="M673.44,588.57h-.05c-1.17.2-2.36.38-3.55.55s-2.41.34-3.61.49-2.43.3-3.64.42-2.45.24-3.67.34q-3.39.29-6.82.44l-.54,0h-.06l-.55,0c-1,0-2,.07-3,.08h-.3c-1.11,0-2.21,0-3.33,0h-1.59A178,178,0,0,1,466,412c0-98.42,79-178.29,176.76-179h1.8c.88,0,1.75,0,2.63,0s2.07,0,3.1.08c.38,0,.76,0,1.13,0,.6,0,1.21,0,1.81.07h.16c1,0,2.05.12,3.08.19a176.84,176.84,0,0,0-82.57,37.36,13.8,13.8,0,0,0-8.33-2.79A14,14,0,0,0,554,289.73q-3.35,3.7-6.47,7.58-5.41,6.7-10.17,13.93a179.6,179.6,0,0,0,71,260A176.13,176.13,0,0,0,673.44,588.57Z" transform="translate(-403.93 -233)"/></g><g class="cls-4"><path class="cls-2" d="M651.44,590.85l.47,0c-1.74.06-3.49.1-5.24.11-.6,0-1.19,0-1.79,0h-2.33a181.36,181.36,0,0,1-46.45-6.63C520.45,563.26,465,494.31,465,412.5c0-97.82,79.28-177.25,177.55-178.49h2.33c.6,0,1.19,0,1.79,0,1.75,0,3.5,0,5.24.11l-.53,0h-.08c-95.8,4-172.23,82.3-172.23,178.34,0,77.58,49.86,143.6,119.53,168.21a180.7,180.7,0,0,0,52.78,10.14Z" transform="translate(-403.93 -233)"/></g><g id="Ebene_3" data-name="Ebene 3"><path class="cls-5" d="M736,466c16.42,18.66,36.86,29.82,62,34-12.09,25-32.82,45.66-58,62-25.76-4.22-40.72-24.1-52-51C708,501.51,725.42,486.64,736,466Z" transform="translate(-403.93 -233)"/><path class="cls-5" d="M599.51,511.26c-16.6,17.16-25.27,38.66-27,62.62-25.39-9-46.8-25-65-47,1.74-24.55,18.62-41.85,43.69-55C562.24,489.72,578.52,503.29,599.51,511.26Z" transform="translate(-403.93 -233)"/><path class="cls-6" d="M563.76,340.32c-11.73-20.93-29.42-37.78-52.45-47,16.71-20.85,40-37.43,67.36-47.38,23.61,9.39,37.44,35.81,42.57,63.36C600.24,313.93,578.12,323.25,563.76,340.32Z" transform="translate(-403.93 -233)"/><path d="M450,399" transform="translate(-403.93 -233)"/><path class="cls-5" d="M690.5,318.5c19.56-16.7,30.09-38.91,34.5-64.5,26.2,12.34,48.41,31.85,65.5,57.5-4.45,26.22-24.32,43-52.5,54.5C728.09,345.62,712.11,329.29,690.5,318.5Z" transform="translate(-403.93 -233)"/></g><path class="cls-7" d="M822.91,412A179,179,0,0,1,673.44,588.57h-.05c-1.18.2-2.37.38-3.57.55s-2.42.34-3.63.49-2.44.3-3.66.42-2.46.24-3.69.34q-3.4.29-6.85.44l-.55,0,.47,0c-1.73.06-3.47.1-5.21.11-.6,0-1.19,0-1.79,0H642.6a179,179,0,0,1-46.37-6.41l.14-.24a41.43,41.43,0,0,1,2.49-3.66,60.28,60.28,0,0,1,9.17-9.47,80.29,80.29,0,0,1,14.47-9.71c14.63-7.56,19.55-3.08,42-10,15.22-4.69,25.09-10.49,43-21,23.09-13.56,34.64-20.34,47-33,11.14-11.41,28.77-29.47,33-56,2.87-18-3.24-22.35,1-45,3.84-20.5,12-33.85,14-37,.87-1.38,4-6.31,8.46-11.92A178.43,178.43,0,0,1,822.91,412Z" transform="translate(-403.93 -233)"/><ellipse class="cls-8" cx="138.12" cy="71.5" rx="8.94" ry="8.5"/><ellipse class="cls-8" cx="133.07" cy="51.5" rx="5" ry="5.5"/><path class="cls-9" d="M579,282a14,14,0,1,1-5.62-11.21A14,14,0,0,1,579,282Z" transform="translate(-403.93 -233)"/></g><g id="Ebene_2" data-name="Ebene 2"><path class="cls-10" d="M708.37,477.38c-9.8,0-17.19-6.53-17.19-15.19,0-9.56,6.09-15,15.88-19.39a8.54,8.54,0,0,1-1.93-3.64c-5.39,5.86-11.62,10.1-19,10.1-9.44,0-16-7.07-16-17.19,0-.34,0-.67,0-1-9,11.33-21.45,18.19-33.5,18.19-7.89,0-14.06-2.53-17.94-7.09-4.33,3.95-9.66,7.09-16,7.09-5.71,0-10.31-2.17-13.31-6-4,3.38-8.95,6-14.71,6-6.39,0-11.4-2.72-14.33-7.4-6.93,4.78-14.58,7.4-22,7.4-9.21,0-16.14-3.56-19.75-9Line truncated

After

Width:  |  Height:  |  Size: 13 KiB

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