mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-12-24 12:35:12 +00:00
Compare commits
19 Commits
841502f9d1
...
0bc02ee9ea
Author | SHA1 | Date | |
---|---|---|---|
0bc02ee9ea | |||
7ac73204df | |||
|
52e38461a4 | ||
|
45b5e04442 | ||
a792c43e24 | |||
|
78e2de6545 | ||
a6a5b58e25 | |||
012de346e8 | |||
f28b301a28 | |||
|
45acbfd9a2 | ||
fe2c064e30 | |||
9821c004ec | |||
b748b9492a | |||
969ac6feaf | |||
be534551ba | |||
c88efc5484 | |||
8eba80f7d4 | |||
92de923fad | |||
83f6018f7e |
|
@ -106,3 +106,29 @@ pricealarmsRouter.put('/', async (req: Request, res: Response) => {
|
||||||
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DELETE pricealarms/:id
|
||||||
|
pricealarmsRouter.delete('/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Authenticate user
|
||||||
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
|
const session_id = (req.query.session_id ?? '').toString();
|
||||||
|
const session_key = (req.query.session_key ?? '').toString();
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
|
const id: number = parseInt(req.params.id, 10);
|
||||||
|
|
||||||
|
const success = await PriceAlarmsService.deletePriceAlarm(id, user.user_id);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
res.status(200).send(JSON.stringify({success: true}));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
res.status(500).send(JSON.stringify({success: false}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error handling a request: ' + e.message);
|
||||||
|
res.status(500).send(JSON.stringify({'message': 'Internal Server Error. Try again later.'}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -92,3 +92,24 @@ export const updatePriceAlarm = async (alarm_id: number, user_id: number, define
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given price alarm
|
||||||
|
* @param alarm_id The id of the price alarm to update
|
||||||
|
* @param user_id The id of the user that wants to update the price alarm
|
||||||
|
*/
|
||||||
|
export const deletePriceAlarm = async (alarm_id: number, user_id: number): Promise<boolean> => {
|
||||||
|
let conn;
|
||||||
|
try {
|
||||||
|
conn = await pool.getConnection();
|
||||||
|
const res = await conn.query('DELETE FROM price_alarms WHERE alarm_id = ? AND user_id = ?', [alarm_id, user_id]);
|
||||||
|
|
||||||
|
return res.affectedRows === 1;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
if (conn) {
|
||||||
|
conn.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class SearchProduct {
|
||||||
//throw new PendingException();
|
//throw new PendingException();
|
||||||
Preconditions.driver.get("https://betterzon.xyz");
|
Preconditions.driver.get("https://betterzon.xyz");
|
||||||
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".logo")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
||||||
|
@ -25,7 +25,7 @@ public class SearchProduct {
|
||||||
searchField.sendKeys(searchTerm);
|
searchField.sendKeys(searchTerm);
|
||||||
searchField.sendKeys(Keys.ENTER);
|
searchField.sendKeys(Keys.ENTER);
|
||||||
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".logo")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see the error page \"([^\"]*)\"$")
|
@Then("^the user should see the error page \"([^\"]*)\"$")
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {NgcCookieConsentService, NgcInitializeEvent, NgcNoCookieLawEvent, NgcStatusChangeEvent} from 'ngx-cookieconsent';
|
import {NgcCookieConsentService, NgcInitializeEvent, NgcNoCookieLawEvent, NgcStatusChangeEvent} from 'ngx-cookieconsent';
|
||||||
import {Subscription} from 'rxjs';
|
import {Subscription} from 'rxjs';
|
||||||
|
import {ApiService} from "./services/api.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
@ -19,12 +21,18 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||||
private revokeChoiceSubscription: Subscription;
|
private revokeChoiceSubscription: Subscription;
|
||||||
private noCookieLawSubscription: Subscription;
|
private noCookieLawSubscription: Subscription;
|
||||||
|
|
||||||
|
isLoggedIn = false;
|
||||||
|
showUserBoard = false;
|
||||||
|
username?: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private ccService: NgcCookieConsentService
|
private ccService: NgcCookieConsentService,
|
||||||
|
private api: ApiService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
// subscribe to cookieconsent observables to react to main events
|
// subscribe to cookieconsent observables to react to main events
|
||||||
this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(
|
this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -38,6 +38,8 @@ import { CopyrightComponent } from './components/copyright/copyright.component';
|
||||||
import { GreetingInfoSliderComponent } from './components/greeting-info-slider/greeting-info-slider.component';
|
import { GreetingInfoSliderComponent } from './components/greeting-info-slider/greeting-info-slider.component';
|
||||||
import { KundenComponent } from './components/kunden/kunden.component';
|
import { KundenComponent } from './components/kunden/kunden.component';
|
||||||
import { AboutUsComponent } from './components/about-us/about-us.component';
|
import { AboutUsComponent } from './components/about-us/about-us.component';
|
||||||
|
import { ProfileComponent } from './components/profile/profile.component';
|
||||||
|
import { ProfilePageComponent } from './pages/profile-page/profile-page.component';
|
||||||
|
|
||||||
// For cookie popup
|
// For cookie popup
|
||||||
const cookieConfig: NgcCookieConsentConfig = {
|
const cookieConfig: NgcCookieConsentConfig = {
|
||||||
|
@ -102,6 +104,8 @@ const cookieConfig: NgcCookieConsentConfig = {
|
||||||
GreetingInfoSliderComponent,
|
GreetingInfoSliderComponent,
|
||||||
KundenComponent,
|
KundenComponent,
|
||||||
AboutUsComponent,
|
AboutUsComponent,
|
||||||
|
ProfileComponent,
|
||||||
|
ProfilePageComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {ImprintComponent} from './pages/imprint/imprint.component';
|
||||||
import {PrivacyComponent} from './pages/privacy/privacy.component';
|
import {PrivacyComponent} from './pages/privacy/privacy.component';
|
||||||
import {SigninComponent} from "./components/auth/signin/signin.component";
|
import {SigninComponent} from "./components/auth/signin/signin.component";
|
||||||
import {RegistrationComponent} from "./components/auth/registration/registration.component";
|
import {RegistrationComponent} from "./components/auth/registration/registration.component";
|
||||||
|
import {ProfileComponent} from "./components/profile/profile.component";
|
||||||
|
import {ProfilePageComponent} from "./pages/profile-page/profile-page.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: LandingpageComponent, pathMatch: 'full'},
|
{path: '', component: LandingpageComponent, pathMatch: 'full'},
|
||||||
|
@ -21,6 +23,7 @@ const routes: Routes = [
|
||||||
{path: 'signin', component: SigninComponent},
|
{path: 'signin', component: SigninComponent},
|
||||||
{path: 'registration', component: RegistrationComponent},
|
{path: 'registration', component: RegistrationComponent},
|
||||||
{path: "product-detail", component: ProductDetailPageComponent},
|
{path: "product-detail", component: ProductDetailPageComponent},
|
||||||
|
{path: "profile", component: ProfilePageComponent},
|
||||||
{path: '**', component: PageNotFoundPageComponent}
|
{path: '**', component: PageNotFoundPageComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- About Section Content-->
|
<!-- About Section Content-->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-4 ms-auto"><p class="lead">text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries</p></div>
|
<div class="col-lg-4 ms-auto"><p class="lead">You follow the same passion as we do and you want to find alternatives to the de-facto monopolist Amazon?</p></div>
|
||||||
<div class="col-lg-4 me-auto"><p class="lead">text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries</p></div>
|
<div class="col-lg-4 me-auto"><p class="lead">In this case, welcome aboard! We’re happy that you share our passion and hope that we can help you achieving this goal with the website</p></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="col-md-8 col-xs-12 col-sm-12 login_form ">
|
<div class="col-md-8 col-xs-12 col-sm-12 login_form ">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h2>Konto erstellen</h2>
|
<h2>Registration</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<form [formGroup]="form" class="form-group" (ngSubmit)="onSubmit()">
|
<form [formGroup]="form" class="form-group" (ngSubmit)="onSubmit()">
|
||||||
|
@ -22,17 +22,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- <span class="fa fa-lock"></span> -->
|
<!-- <span class="fa fa-lock"></span> -->
|
||||||
<input type="password" formControlName="password" name="password" id="password" class="form__input" placeholder="Kennwort">
|
<input type="password" formControlName="password" name="password" id="password" class="form__input" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="password" name="password" id="password_repeated" class="form__input" placeholder="Kennwort bestätigen">
|
<input type="password" name="password" id="password_repeated" class="form__input" placeholder="Kennwort bestätigen">
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="submit" value="Erstellen" class="btn_signin">
|
<input type="submit" value="Sign up" class="btn_signin">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a href="/signin">Sich anmelden</a>
|
<p>Have an account?<a href="/signin">Log In</a></p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import {ApiService} from "../../../services/api.service";
|
import {ApiService} from "../../../services/api.service";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -15,7 +16,8 @@ export class RegistrationComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private api : ApiService
|
private api : ApiService,
|
||||||
|
private router: Router
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -32,6 +34,11 @@ export class RegistrationComponent implements OnInit {
|
||||||
get me() { return this.form.controls; }
|
get me() { return this.form.controls; }
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.api.registerUser(this.form.value.username, this.form.value.password, this.form.value.email).subscribe(res=>console.log(res));
|
this.api.registerUser(this.form.value.username, this.form.value.password, this.form.value.email).subscribe(
|
||||||
|
res=> {
|
||||||
|
this.api.saveSessionInfoToLocalStorage(res);
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="col-md-8 col-xs-12 col-sm-12 login_form ">
|
<div class="col-md-8 col-xs-12 col-sm-12 login_form ">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h2>Anmelden</h2>
|
<h2>Sign In</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<form [formGroup]="loginForm" class="form-group" (ngSubmit)="onSubmit()">
|
<form [formGroup]="loginForm" class="form-group" (ngSubmit)="onSubmit()">
|
||||||
|
@ -18,12 +18,12 @@
|
||||||
<input type="password" formControlName="password" name="password" id="password" class="form__input" placeholder="Password">
|
<input type="password" formControlName="password" name="password" id="password" class="form__input" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="submit" value="Anmelden" class="btn_signin">
|
<input type="submit" value="Log in" class="btn_signin">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a href="/registration">Konto erstellen</a>
|
<p>No account yet?<a href="/registration">sign up</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,7 +18,6 @@ export class SigninComponent implements OnInit {
|
||||||
private isSignUpFailed: boolean;
|
private isSignUpFailed: boolean;
|
||||||
private errorMessage: '';
|
private errorMessage: '';
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
|
@ -43,9 +42,8 @@ export class SigninComponent implements OnInit {
|
||||||
this.api.loginUser(this.loginForm.value.username, this.loginForm.value.password)
|
this.api.loginUser(this.loginForm.value.username, this.loginForm.value.password)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
data => {
|
data => {
|
||||||
this.router.navigate(['']);
|
|
||||||
this.isSuccessful = true;
|
this.isSuccessful = true;
|
||||||
this.isSignUpFailed = false;
|
this.router.navigate(['']);
|
||||||
this.api.saveSessionInfoToLocalStorage(data);
|
this.api.saveSessionInfoToLocalStorage(data);
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
|
|
|
@ -5,24 +5,22 @@
|
||||||
<div class="col-lg-4 mb-5 mb-lg-0">
|
<div class="col-lg-4 mb-5 mb-lg-0">
|
||||||
<h4 class="text-uppercase mb-4">Location</h4>
|
<h4 class="text-uppercase mb-4">Location</h4>
|
||||||
<p class="lead mb-0">
|
<p class="lead mb-0">
|
||||||
70376 Stuttgart
|
76133 Karlsruhe
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- Footer Social Icons-->
|
<!-- Footer Social Icons-->
|
||||||
<div class="col-lg-4 mb-5 mb-lg-0">
|
<div class="col-lg-4 mb-5 mb-lg-0">
|
||||||
<h4 class="text-uppercase mb-4">FOLGE UNS</h4>
|
<h4 class="text-uppercase mb-4">FOLLOW US</h4>
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-github"></i></a>
|
<a class="btn btn-outline-light btn-social mx-1" href="https://github.com/Mueller-Patrick/Betterzon"><i class="fab fa-fw fa-github"></i></a>
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-twitter"></i></a>
|
<a class="btn btn-outline-light btn-social mx-1" href="https://blog.betterzon.xyz/"><i class="fab fa-fw fa-dribbble"></i></a>
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-linkedin-in"></i></a>
|
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-dribbble"></i></a>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Footer About Text-->
|
<!-- Footer About Text-->
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<h4 class="text-uppercase mb-4">SOME INFO</h4>
|
<h4 class="text-uppercase mb-4">CONTACT US</h4>
|
||||||
<p class="lead mb-0">
|
<p class="lead mb-0">
|
||||||
text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
|
betterzon-contact@mueller-patrick.tech
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<section class="page-section portfolio" id="top-gesuchte">
|
<section class="page-section portfolio" id="top-gesuchte">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Portfolio Section Heading-->
|
<!-- Portfolio Section Heading-->
|
||||||
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">TOP-ANGEBOTE</h2>
|
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">TOP-SEARCHES</h2>
|
||||||
<!-- Icon Divider-->
|
<!-- Icon Divider-->
|
||||||
<div class="divider-custom">
|
<div class="divider-custom">
|
||||||
<div class="divider-custom-line"></div>
|
<div class="divider-custom-line"></div>
|
||||||
|
@ -9,18 +9,18 @@
|
||||||
<!-- Portfolio Grid Items-->
|
<!-- Portfolio Grid Items-->
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<!-- Portfolio Item 1-->
|
<!-- Portfolio Item 1-->
|
||||||
<div class="col-md-4 mx-auto my-5" *ngFor="let product of products" (click)="clickedProduct(product)">
|
<div class="col-md-4 mx-auto my-5" *ngFor="let productId of bestDealsProductIds" (click)="clickedProduct(productId)">
|
||||||
<div class="bbb_deals_wrapper">
|
<div class="bbb_deals_wrapper">
|
||||||
<div class="bbb_deals_image"><img src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg" alt=""></div>
|
<div class="bbb_deals_image"><img src="https://www.mueller-patrick.tech/betterzon/images/{{productsPricesMap[productId]?.product?.image_guid}}.jpg" alt=""></div>
|
||||||
<div class="bbb_deals_content">
|
<div class="bbb_deals_content">
|
||||||
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
||||||
<div class="bbb_deals_item_name">{{product.name}}</div>
|
<div class="bbb_deals_item_name">{{productsPricesMap[productId]?.product?.name}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
||||||
<div class="bbb_deals_item_category">Amazon: <span id="bbb_deals_item_price_a"><strike>{{product.price}}$</strike></span></div>
|
<div class="bbb_deals_item_category">Amazon: <span id="bbb_deals_item_price_a"><strike>{{productsPricesMap[productId]?.amazonPrice?.price_in_cents/100}}€</strike></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
<div class="bbb_deals_info_line d-flex flex-row justify-content-start">
|
||||||
<div class="bbb_deals_item_category">Plantshub: <span id="bbb_deals_item_price_b">599,00$</span></div>
|
<div class="bbb_deals_item_category">{{productsPricesMap[productId]?.vendor?.name}}: <span id="bbb_deals_item_price_b">{{productsPricesMap[productId]?.lowestPrice?.price_in_cents/100}}€</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="available_bar">
|
<div class="available_bar">
|
||||||
<span style="width:17%"></span>
|
<span style="width:17%"></span>
|
||||||
|
|
|
@ -11,6 +11,9 @@ import {ActivatedRoute, Router} from '@angular/router';
|
||||||
export class HotDealsWidgetComponent implements OnInit {
|
export class HotDealsWidgetComponent implements OnInit {
|
||||||
|
|
||||||
products: Product[] = [];
|
products: Product[] = [];
|
||||||
|
bestDealsProductIds = [];
|
||||||
|
amazonPrices = [];
|
||||||
|
productsPricesMap = new Map();
|
||||||
@Input() numberOfProducts: number;
|
@Input() numberOfProducts: number;
|
||||||
@Input() showProductPicture: boolean;
|
@Input() showProductPicture: boolean;
|
||||||
@Input() searchQuery: string;
|
@Input() searchQuery: string;
|
||||||
|
@ -24,12 +27,13 @@ export class HotDealsWidgetComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadParams();
|
|
||||||
|
this.getBestDeals();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadParams(): void {
|
loadParams(): void {
|
||||||
if (!this.numberOfProducts) {
|
if (!this.numberOfProducts) {
|
||||||
this.numberOfProducts = 10;
|
this.numberOfProducts = 9;
|
||||||
}
|
}
|
||||||
if (!this.showProductPicture) {
|
if (!this.showProductPicture) {
|
||||||
this.showProductPicture = false;
|
this.showProductPicture = false;
|
||||||
|
@ -43,26 +47,69 @@ export class HotDealsWidgetComponent implements OnInit {
|
||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'search': {
|
case 'search': {
|
||||||
this.getSearchedProducts();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
this.getProducts();
|
this.getProductsByIds();
|
||||||
|
this.getAmazonPricesForBestDeals();
|
||||||
|
this.getVendors()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getProducts(): void {
|
getProductsByIds(): void {
|
||||||
this.apiService.getProducts().subscribe(products => this.products = products);
|
this.apiService.getProductsByIds(this.bestDealsProductIds).subscribe(
|
||||||
|
products => {
|
||||||
|
products.forEach(product => {
|
||||||
|
this.productsPricesMap [product.product_id].product = product;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBestDeals(): void {
|
||||||
|
this.apiService.getBestDeals(9).subscribe(
|
||||||
|
deals => {
|
||||||
|
deals.forEach(deal => {
|
||||||
|
this.bestDealsProductIds.push(deal.product_id);
|
||||||
|
this.productsPricesMap [deal.product_id] = {lowestPrice: deal}
|
||||||
|
});
|
||||||
|
this.loadParams();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVendors(): void {
|
||||||
|
this.bestDealsProductIds.forEach(
|
||||||
|
productId => {
|
||||||
|
const currentDeal = this.productsPricesMap[productId].lowestPrice;
|
||||||
|
this.apiService.getVendorById(currentDeal.vendor_id).subscribe(
|
||||||
|
vendor => {
|
||||||
|
this.productsPricesMap[productId].vendor = vendor;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getAmazonPricesForBestDeals(): void{
|
||||||
|
this.bestDealsProductIds.forEach(id => {
|
||||||
|
this.apiService.getAmazonPrice(id).subscribe(
|
||||||
|
price => {
|
||||||
|
this.amazonPrices.push(price);
|
||||||
|
this.productsPricesMap[price[0].product_id].amazonPrice = price[0];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSearchedProducts(): void {
|
getSearchedProducts(): void {
|
||||||
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => this.products = products);
|
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => this.products = products);
|
||||||
}
|
}
|
||||||
|
|
||||||
clickedProduct(product: Product): void {
|
clickedProduct(productId: string): void {
|
||||||
this.router.navigate([('/product/' + product.product_id)]);
|
this.router.navigate([('/product/' + productId)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<section class="page-section portfolio" id="unsere-kunden">
|
<section class="page-section portfolio" id="unsere-kunden">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- Portfolio Section Heading-->
|
<!-- Portfolio Section Heading-->
|
||||||
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">SIE VERTRAUEN UNS</h2>
|
<h2 class="page-section-heading text-center text-uppercase text-secondary mb-0">THEY TRUST US</h2>
|
||||||
<!-- Icon Divider-->
|
<!-- Icon Divider-->
|
||||||
<div class="divider-custom">
|
<div class="divider-custom">
|
||||||
<div class="divider-custom-line"></div>
|
<div class="divider-custom-line"></div>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
<header class="masthead bg-transparent text-white text-center" id="w1">
|
||||||
|
|
||||||
|
</header>
|
||||||
<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"/>
|
||||||
|
@ -20,8 +23,12 @@
|
||||||
{{product?.short_description}}
|
{{product?.short_description}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="priceAlarm">
|
<div class="priceAlarm" *ngIf="!isLoggedIn" routerLink="/signin">
|
||||||
Set Price Alarm
|
Login to set a price alarm
|
||||||
|
</div>
|
||||||
|
<div class="priceAlarm" *ngIf="isLoggedIn">
|
||||||
|
<input type="search" id="s" name="price" [(ngModel)]="price">
|
||||||
|
<div (click)="setPriceAlarm()">Set Price Alarm</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bestPriceContainer">
|
<div class="bestPriceContainer">
|
||||||
<div class="bestPrice">
|
<div class="bestPrice">
|
||||||
|
|
|
@ -34,6 +34,8 @@ export class ProductDetailsComponent implements OnInit {
|
||||||
vendorMap = {};
|
vendorMap = {};
|
||||||
@ViewChild('chart') chart: ChartComponent;
|
@ViewChild('chart') chart: ChartComponent;
|
||||||
public chartOptions: ChartOptions;
|
public chartOptions: ChartOptions;
|
||||||
|
isLoggedIn: boolean;
|
||||||
|
price: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService
|
private apiService: ApiService
|
||||||
|
@ -44,6 +46,9 @@ export class ProductDetailsComponent implements OnInit {
|
||||||
this.getProduct();
|
this.getProduct();
|
||||||
this.getVendors();
|
this.getVendors();
|
||||||
this.getPrices();
|
this.getPrices();
|
||||||
|
if (this.apiService.getSessionInfoFromLocalStorage().session_id != "") {
|
||||||
|
this.isLoggedIn = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getProduct(): void {
|
getProduct(): void {
|
||||||
|
@ -117,4 +122,12 @@ export class ProductDetailsComponent implements OnInit {
|
||||||
|
|
||||||
return Math.round(percentage);
|
return Math.round(percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPriceAlarm() {
|
||||||
|
this.apiService.createPriceAlarms(this.productId, this.price*100).subscribe(
|
||||||
|
alarms => console.log(alarms)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,30 @@
|
||||||
<div *ngIf="products.length==0">
|
<div *ngIf="products.length==0">
|
||||||
No Products found!
|
No Products found!
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container mt-5 mb-5">
|
||||||
|
<div class="d-flex justify-content-center row">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="row p-2 bg-white border rounded" *ngFor="let product of products">
|
||||||
|
<div class="col-md-3 mt-1"><img width="50%" class="img-fluid img-responsive rounded product-image" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"></div>
|
||||||
|
<div class="col-md-6 mt-1">
|
||||||
|
<h5>{{product.name}}</h5>
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<p class="text-justify text-truncate para mb-0">{{product.short_description}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 mb-1 spec-1"><span></span><span class="dot"></span><span></span><span class="dot"></span><span><br></span></div>
|
||||||
|
<div class="mt-1 mb-1 spec-1"><span></span><span class="dot"></span><span></span><span class="dot"></span><span><br></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="align-items-center align-content-center col-md-3 border-left mt-1">
|
||||||
|
<div class="d-flex flex-row align-items-center">
|
||||||
|
<h4 class="mr-1">${{pricesMap[product.product_id]?.price_in_cents/100}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column mt-4"><button class="btn btn-primary btn-sm" type="button" (click)="clickedProduct(product)">Details</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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"/>
|
||||||
|
@ -20,3 +44,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {ActivatedRoute, Router} from '@angular/router';
|
||||||
})
|
})
|
||||||
export class ProductListComponent implements OnInit {
|
export class ProductListComponent implements OnInit {
|
||||||
products: Product[] = [];
|
products: Product[] = [];
|
||||||
|
pricesMap: any = {};
|
||||||
@Input() numberOfProducts: number;
|
@Input() numberOfProducts: number;
|
||||||
@Input() showProductPicture: boolean;
|
@Input() showProductPicture: boolean;
|
||||||
@Input() searchQuery: string;
|
@Input() searchQuery: string;
|
||||||
|
@ -53,15 +54,35 @@ export class ProductListComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
getProducts(): void {
|
getProducts(): void {
|
||||||
this.apiService.getProducts().subscribe(products => this.products = products);
|
this.apiService.getProducts().subscribe(products => {
|
||||||
|
this.products = products;
|
||||||
|
this.getPrices();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPrices(): void {
|
||||||
|
this.products.forEach(
|
||||||
|
product => {
|
||||||
|
this.apiService.getLowestPrices(product.product_id).subscribe(
|
||||||
|
prices => {
|
||||||
|
this.pricesMap[product.product_id] = prices[prices.length - 1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getSearchedProducts(): void {
|
getSearchedProducts(): void {
|
||||||
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => this.products = products);
|
this.apiService.getProductsByQuery(this.searchQuery).subscribe(products => {
|
||||||
|
this.products = products;
|
||||||
|
this.getPrices();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clickedProduct(product: Product): void {
|
clickedProduct(product: Product): void {
|
||||||
this.router.navigate([('/product/' + product.product_id)]);
|
this.router.navigate([('/product/' + product.product_id)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
21
Frontend/src/app/components/profile/profile.component.css
Normal file
21
Frontend/src/app/components/profile/profile.component.css
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
.inf-content{
|
||||||
|
border:1px solid #DDDDDD;
|
||||||
|
-webkit-border-radius:10px;
|
||||||
|
-moz-border-radius:10px;
|
||||||
|
border-radius:10px;
|
||||||
|
box-shadow: 7px 7px 7px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-in-page {
|
||||||
|
padding-top: calc(1rem + 20px);
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
table, th, td {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #0d5a4b;
|
||||||
|
}
|
137
Frontend/src/app/components/profile/profile.component.html
Normal file
137
Frontend/src/app/components/profile/profile.component.html
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<div class="container bootstrap snippets bootdey">
|
||||||
|
<div class="panel-body inf-content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<img alt="" style="width:600px;" title="" class="img-circle img-thumbnail isTooltip" src="https://bootdey.com/img/Content/avatar/avatar7.png" data-original-title="Usuario">
|
||||||
|
<ul title="Ratings" class="list-inline ratings text-center">
|
||||||
|
<li><a href="#"><span class="glyphicon glyphicon-star"></span></a></li>
|
||||||
|
<li><a href="#"><span class="glyphicon glyphicon-star"></span></a></li>
|
||||||
|
<li><a href="#"><span class="glyphicon glyphicon-star"></span></a></li>
|
||||||
|
<li><a href="#"><span class="glyphicon glyphicon-star"></span></a></li>
|
||||||
|
<li><a href="#"><span class="glyphicon glyphicon-star"></span></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<strong>Information</strong><br>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-user-information">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>
|
||||||
|
<span class="glyphicon glyphicon-bookmark text-primary"></span>
|
||||||
|
Username
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
<td class="text-primary">
|
||||||
|
{{currentUser.username}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>
|
||||||
|
<span class="glyphicon glyphicon-eye-open text-primary"></span>
|
||||||
|
Role
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
<td class="text-primary">
|
||||||
|
User
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>
|
||||||
|
<span class="glyphicon glyphicon-envelope text-primary"></span>
|
||||||
|
Email
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
<td class="text-primary">
|
||||||
|
{{currentUser.email}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>
|
||||||
|
<span class="glyphicon glyphicon-calendar text-primary"></span>
|
||||||
|
created
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
<td class="text-primary">
|
||||||
|
{{currentUser.registration_date}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<header class="header-in-page">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<div class="container bootstrap snippets bootdey">
|
||||||
|
<div class="col-auto">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<tr>
|
||||||
|
<th>Product</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Change</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let alarm of alarms">
|
||||||
|
<td>
|
||||||
|
{{productsMap[alarm.product_id]?.name}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{alarm.defined_price/100}}€
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img class="delete" src="../assets/images/Delete_icon-icons.com_55931.png" (click)="delete(alarm.alarm_id)">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img class="delete" src="../assets/images/Delete_icon-icons.com_55931.png" (click)="delete(alarm.alarm_id)">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
<div class="container" *ngIf="currentUser; else loggedOut">
|
||||||
|
<p>
|
||||||
|
<strong>e-mail</strong>
|
||||||
|
{{ currentUser.email}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>username:</strong>
|
||||||
|
{{ currentUser.username}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>alarms</strong>
|
||||||
|
{{alarms}}
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Produkt</th>
|
||||||
|
<th>Preis</th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let alarm of alarms">
|
||||||
|
<td>
|
||||||
|
{{productsMap[alarm.product_id]?.name}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{alarm.defined_price/100}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
<strong><a routerLink="/">zurück</a></strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #loggedOut>
|
||||||
|
Please login.
|
||||||
|
</ng-template>
|
||||||
|
-->
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfileComponent } from './profile.component';
|
||||||
|
|
||||||
|
describe('ProfileComponent', () => {
|
||||||
|
let component: ProfileComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProfileComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
58
Frontend/src/app/components/profile/profile.component.ts
Normal file
58
Frontend/src/app/components/profile/profile.component.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {ApiService} from "../../services/api.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile',
|
||||||
|
templateUrl: './profile.component.html',
|
||||||
|
styleUrls: ['./profile.component.css']
|
||||||
|
})
|
||||||
|
export class ProfileComponent implements OnInit {
|
||||||
|
|
||||||
|
currentUser: any;
|
||||||
|
obj:any;
|
||||||
|
alarms: any [];
|
||||||
|
productsMap: any = {};
|
||||||
|
|
||||||
|
constructor(private api: ApiService ) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
|
||||||
|
this.api.getUserInfo().subscribe(
|
||||||
|
user=> {
|
||||||
|
this.currentUser = user
|
||||||
|
console.log(this.currentUser);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.getPriceAlarms();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPriceAlarms(): void {
|
||||||
|
this.api.getPriceAlarms().subscribe(
|
||||||
|
alarms => {
|
||||||
|
this.alarms = alarms
|
||||||
|
this.getProductsByIds()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getProductsByIds(): void {
|
||||||
|
let productIds: number [] = [];
|
||||||
|
this.alarms.forEach(
|
||||||
|
alarm => {productIds.push(alarm.product_id)}
|
||||||
|
);
|
||||||
|
this.api.getProductsByIds(productIds).subscribe(
|
||||||
|
products => {
|
||||||
|
products.forEach(
|
||||||
|
product => {this.productsMap[product.product_id] = product}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id:number): void {
|
||||||
|
this.api.deletePriceAlarm(id).subscribe(
|
||||||
|
res => window.location.reload()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
<nav class="navbar navbar-expand-lg bg-secondary text-uppercase fixed-top" id="mainNav">
|
<nav class="navbar navbar-expand-lg bg-secondary text-uppercase fixed-top" id="mainNav">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" routerLink=""> Betterzon</a>
|
<a class="navbar-brand" routerLink=""> Betterzon</a>
|
||||||
<form class="form-inline my-2 my-lg-0">
|
<div class="form-inline my-2 my-lg-0">
|
||||||
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
|
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" (keyup.enter)="getSearchedProducts()" [(ngModel)]="searchQuery">
|
||||||
</form>
|
</div>
|
||||||
<button class="navbar-toggler text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler text-uppercase font-weight-bold bg-primary text-white rounded" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
Menu
|
Menu
|
||||||
<i class="fas fa-bars"></i>
|
<i class="fas fa-bars"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||||
<ul class="navbar-nav ms-auto">
|
<ul class="navbar-nav ms-auto">
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#top-gesuchte">Top-Gesuchte</a></li>
|
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#top-gesuchte">top-searches</a></li>
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#about">über uns</a></li>
|
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#about">about</a></li>
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#unsere-kunden">Unsere Kunden</a></li>
|
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="#unsere-kunden">our clients</a></li>
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="/signin">Anmelden</a></li>
|
<li class="nav-item mx-0 mx-lg-1" *ngIf="!isLoggedIn"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="/signin">sign in</a></li>
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="/registration">Konto Erstellen</a></li>
|
<li class="nav-item mx-0 mx-lg-1" *ngIf="!isLoggedIn"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="/registration">sign up</a></li>
|
||||||
|
<li class="nav-item mx-0 mx-lg-1" *ngIf="isLoggedIn"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="" (click)="logout()">log out</a></li>
|
||||||
|
<li class="nav-item mx-0 mx-lg-1" *ngIf="isLoggedIn"><a class="nav-link py-3 px-0 px-lg-3 rounded" routerLink="/profile">profile</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {ApiService} from "../../services/api.service";
|
import {ApiService} from "../../services/api.service";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -10,13 +11,39 @@ import {ApiService} from "../../services/api.service";
|
||||||
export class TopBarComponent implements OnInit {
|
export class TopBarComponent implements OnInit {
|
||||||
|
|
||||||
sidenav: any;
|
sidenav: any;
|
||||||
|
isLoggedIn: boolean;
|
||||||
|
searchQuery: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private api: ApiService
|
private api: ApiService,
|
||||||
) { }
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
||||||
this.api.getUserInfo().subscribe(data=>{console.log(data)});
|
this.api.getUserInfo().subscribe(data => {
|
||||||
|
console.log(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.api.getSessionInfoFromLocalStorage().session_id != "") {
|
||||||
|
this.isLoggedIn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logout(): void {
|
||||||
|
localStorage.setItem('session_id', '');
|
||||||
|
localStorage.setItem('session_key', '');
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
getSearchedProducts(): void {
|
||||||
|
console.log(this.searchQuery);
|
||||||
|
this.redirectTo('/search', {queryParams: {q: this.searchQuery}});
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectTo(uri: string, queryParams: object): void {
|
||||||
|
this.router.navigateByUrl('/', {skipLocationChange: true}).then(() =>
|
||||||
|
this.router.navigate([uri], queryParams));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
#mainComponents {
|
|
||||||
margin: 5em;
|
|
||||||
margin-top: .5em;
|
|
||||||
margin-bottom: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#productListsContainer {
|
|
||||||
display: grid;
|
|
||||||
grid-template-areas:
|
|
||||||
'search search'
|
|
||||||
'popularSearches bestDeals';
|
|
||||||
grid-template-columns: 50% 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchContainer {
|
|
||||||
position: relative;
|
|
||||||
grid-area: search;
|
|
||||||
height: 10em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchContainer input {
|
|
||||||
position: relative;
|
|
||||||
font-size: 1.5em;
|
|
||||||
padding: .25em;
|
|
||||||
display: block;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: auto;
|
|
||||||
-ms-transform: translateY(50%);
|
|
||||||
transform: translateY(2.5em);
|
|
||||||
}
|
|
||||||
|
|
||||||
#popularSearchesList {
|
|
||||||
grid-area: popularSearches;
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#popularSearchesList h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bestDealsList {
|
|
||||||
grid-area: bestDeals;
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bestDealsList h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
import {ApiService} from "../../services/api.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-landingpage',
|
selector: 'app-landingpage',
|
||||||
|
@ -8,13 +9,16 @@ import {Router} from '@angular/router';
|
||||||
})
|
})
|
||||||
export class LandingpageComponent implements OnInit {
|
export class LandingpageComponent implements OnInit {
|
||||||
searchInput: string;
|
searchInput: string;
|
||||||
|
isLoggedIn = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router
|
private router: Router,
|
||||||
|
private api: ApiService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startedSearch(): void {
|
startedSearch(): void {
|
||||||
|
@ -25,5 +29,4 @@ export class LandingpageComponent implements OnInit {
|
||||||
this.router.navigateByUrl('/', {skipLocationChange: true}).then(() =>
|
this.router.navigateByUrl('/', {skipLocationChange: true}).then(() =>
|
||||||
this.router.navigate([uri], queryParams));
|
this.router.navigate([uri], queryParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,43 @@
|
||||||
#mainComponents {
|
body {
|
||||||
margin: 5em;
|
background: #eee
|
||||||
margin-top: .5em;
|
|
||||||
margin-bottom: .5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ratings i {
|
||||||
|
font-size: 16px;
|
||||||
|
color: red
|
||||||
|
}
|
||||||
|
|
||||||
|
.strike-text {
|
||||||
|
color: red;
|
||||||
|
text-decoration: line-through
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-image {
|
||||||
|
width: 20%;
|
||||||
|
height: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
height: 7px;
|
||||||
|
width: 7px;
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
|
margin-top: 3px;
|
||||||
|
background-color: blue;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
|
||||||
|
.spec-1 {
|
||||||
|
color: #938787;
|
||||||
|
font-size: 15px
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-weight: 400
|
||||||
|
}
|
||||||
|
|
||||||
|
.para {
|
||||||
|
font-size: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
|
<app-top-bar></app-top-bar>
|
||||||
|
<header class="masthead bg-transparent text-white text-center" id="w1">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
|
||||||
<div id="mainComponents">
|
<div id="mainComponents">
|
||||||
<app-product-list numberOfProducts="20" [showProductPicture]="true" searchQuery="{{searchTerm}}"
|
<app-product-list numberOfProducts="20" [showProductPicture]="true" searchQuery="{{searchTerm}}"
|
||||||
type="search"></app-product-list>
|
type="search"></app-product-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-footer></app-footer>
|
<header class="masthead bg-transparent text-white text-center">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<app-bottom-bar></app-bottom-bar>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
.header-in-page {
|
||||||
|
padding-top: calc(2rem + 20px);
|
||||||
|
padding-bottom: 6rem;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<app-top-bar></app-top-bar>
|
||||||
|
<header class="header-in-page">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<app-profile></app-profile>
|
||||||
|
<header class="header-in-page">
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<app-bottom-bar></app-bottom-bar>
|
||||||
|
<app-copyright></app-copyright>
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfilePageComponent } from './profile-page.component';
|
||||||
|
|
||||||
|
describe('ProfilePageComponent', () => {
|
||||||
|
let component: ProfilePageComponent;
|
||||||
|
let fixture: ComponentFixture<ProfilePageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProfilePageComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfilePageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile-page',
|
||||||
|
templateUrl: './profile-page.component.html',
|
||||||
|
styleUrls: ['./profile-page.component.css']
|
||||||
|
})
|
||||||
|
export class ProfilePageComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -445,6 +445,25 @@ export class ApiService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given price alarm
|
||||||
|
* @param alarmId the price alarm to delete
|
||||||
|
* @return Observable<any> The observable response of the api
|
||||||
|
*/
|
||||||
|
deletePriceAlarm(alarmId: number): Observable<any> {
|
||||||
|
try {
|
||||||
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
let params = new HttpParams();
|
||||||
|
params = params.append('session_id', sessionInfo.session_id);
|
||||||
|
params = params.append('session_key', sessionInfo.session_key);
|
||||||
|
|
||||||
|
return this.http.delete((this.apiUrl + '/pricealarms/' + alarmId), {params});
|
||||||
|
} catch (exception) {
|
||||||
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* __ __
|
/* __ __
|
||||||
/ / / /_______ __________
|
/ / / /_______ __________
|
||||||
|
|
BIN
Frontend/src/assets/images/Delete_icon-icons.com_55931.png
Normal file
BIN
Frontend/src/assets/images/Delete_icon-icons.com_55931.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 239 B |
|
@ -447,8 +447,8 @@ progress {
|
||||||
}
|
}
|
||||||
|
|
||||||
.lead {
|
.lead {
|
||||||
font-size: 1.25rem;
|
font-size: 1.30rem;
|
||||||
font-weight: 300;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-1 {
|
.display-1 {
|
||||||
|
|
|
@ -5,7 +5,7 @@ Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
||||||
|
|
||||||
# Code Quality
|
# Code Quality
|
||||||
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/88e47ebf837b43af9d12147c22f77f7f)](https://www.codacy.com/gh/Mueller-Patrick/Betterzon/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Mueller-Patrick/Betterzon&utm_campaign=Badge_Grade)
|
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/88e47ebf837b43af9d12147c22f77f7f)](https://www.codacy.com/gh/Mueller-Patrick/Betterzon/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Mueller-Patrick/Betterzon&utm_campaign=Badge_Grade)
|
||||||
[![Code Coverage](https://img.shields.io/badge/coverage-82%25-green)](https://ci.betterzon.xyz)
|
[![Code Coverage](https://img.shields.io/badge/coverage-71%25-green)](https://ci.betterzon.xyz)
|
||||||
|
|
||||||
# Project Status
|
# Project Status
|
||||||
[![Website Status](https://img.shields.io/website?label=www.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fwww.betterzon.xyz)](https://www.betterzon.xyz)
|
[![Website Status](https://img.shields.io/website?label=www.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fwww.betterzon.xyz)](https://www.betterzon.xyz)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user