mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-11-10 00:23:58 +00:00
BETTERZON-42: Functionality and design of various components for the search product UC
This commit is contained in:
parent
e83b2d7511
commit
acdc9a4e92
|
@ -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]
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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({
|
@Component({
|
||||||
selector: 'app-header',
|
selector: 'app-header',
|
||||||
templateUrl: './header.component.html',
|
templateUrl: './header.component.html',
|
||||||
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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)]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#mainComponents {
|
||||||
|
margin: 5em;
|
||||||
|
margin-top: .5em;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
<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"></app-product-list>
|
||||||
|
</div>
|
||||||
<app-footer></app-footer>
|
<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-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>
|
<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-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>
|
||||||
|
|
|
@ -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`);
|
||||||
|
|
1
Frontend/src/assets/images/Betterzon.svg
Normal file
1
Frontend/src/assets/images/Betterzon.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user