mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-11-24 15:23:58 +00:00
Merge pull request #26 from Mueller-Patrick/develop
Master Deployment Construction #1
This commit is contained in:
commit
aaf829f090
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -26,6 +26,7 @@ speed-measure-plugin*.json
|
||||||
!Frontend.iml
|
!Frontend.iml
|
||||||
!Backend.iml
|
!Backend.iml
|
||||||
!CucumberTests.iml
|
!CucumberTests.iml
|
||||||
|
!Crawler.iml
|
||||||
|
|
||||||
# Include IntelliJ modules
|
# Include IntelliJ modules
|
||||||
!/.idea/modules.xml
|
!/.idea/modules.xml
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/Backend/Backend.iml" filepath="$PROJECT_DIR$/Backend/Backend.iml" />
|
<module fileurl="file://$PROJECT_DIR$/Backend/Backend.iml" filepath="$PROJECT_DIR$/Backend/Backend.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/Betterzon.iml" filepath="$PROJECT_DIR$/Betterzon.iml" />
|
<module fileurl="file://$PROJECT_DIR$/Betterzon.iml" filepath="$PROJECT_DIR$/Betterzon.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/Crawler/Crawler.iml" filepath="$PROJECT_DIR$/Crawler/Crawler.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/CucumberTests/CucumberTests.iml" filepath="$PROJECT_DIR$/CucumberTests/CucumberTests.iml" />
|
<module fileurl="file://$PROJECT_DIR$/CucumberTests/CucumberTests.iml" filepath="$PROJECT_DIR$/CucumberTests/CucumberTests.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/Frontend/Frontend.iml" filepath="$PROJECT_DIR$/Frontend/Frontend.iml" />
|
<module fileurl="file://$PROJECT_DIR$/Frontend/Frontend.iml" filepath="$PROJECT_DIR$/Frontend/Frontend.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const categoriesRouter = express.Router();
|
||||||
* Controller Definitions
|
* Controller Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// GET items/
|
// GET categories/
|
||||||
|
|
||||||
categoriesRouter.get('/', async (req: Request, res: Response) => {
|
categoriesRouter.get('/', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
@ -31,7 +31,7 @@ categoriesRouter.get('/', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET items/:id
|
// GET categories/:id
|
||||||
|
|
||||||
categoriesRouter.get('/:id', async (req: Request, res: Response) => {
|
categoriesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
const id: number = parseInt(req.params.id, 10);
|
const id: number = parseInt(req.params.id, 10);
|
||||||
|
@ -50,7 +50,7 @@ categoriesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET items/:name
|
// GET categories/search/:term
|
||||||
|
|
||||||
categoriesRouter.get('/search/:term', async (req: Request, res: Response) => {
|
categoriesRouter.get('/search/:term', async (req: Request, res: Response) => {
|
||||||
const term: string = req.params.term;
|
const term: string = req.params.term;
|
||||||
|
|
|
@ -4,4 +4,7 @@ export interface Price {
|
||||||
vendor_id: number;
|
vendor_id: number;
|
||||||
price_in_cents: number;
|
price_in_cents: number;
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
|
// Only for deals
|
||||||
|
amazonDifference?: number;
|
||||||
|
amazonDifferencePercent?: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const pricesRouter = express.Router();
|
||||||
* Controller Definitions
|
* Controller Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// GET items/
|
// GET prices/
|
||||||
|
|
||||||
pricesRouter.get('/', async (req: Request, res: Response) => {
|
pricesRouter.get('/', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
@ -44,7 +44,7 @@ pricesRouter.get('/', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET items/:id
|
// GET prices/:id
|
||||||
|
|
||||||
pricesRouter.get('/:id', async (req: Request, res: Response) => {
|
pricesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
const id: number = parseInt(req.params.id, 10);
|
const id: number = parseInt(req.params.id, 10);
|
||||||
|
@ -63,6 +63,25 @@ pricesRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GET prices/bestDeals
|
||||||
|
|
||||||
|
pricesRouter.get('/bestDeals/:amount', async (req: Request, res: Response) => {
|
||||||
|
const amount: number = parseInt(req.params.amount, 10);
|
||||||
|
|
||||||
|
if (!amount) {
|
||||||
|
res.status(400).send('Missing parameters.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prices: Prices = await PriceService.getBestDeals(amount);
|
||||||
|
|
||||||
|
res.status(200).send(prices);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(404).send(e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// POST items/
|
// POST items/
|
||||||
|
|
||||||
// pricesRouter.post('/', async (req: Request, res: Response) => {
|
// pricesRouter.post('/', async (req: Request, res: Response) => {
|
||||||
|
|
|
@ -186,6 +186,100 @@ export const findByVendor = async (product: string, vendor: string, type: string
|
||||||
return priceRows;
|
return priceRows;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getBestDeals = async (amount: number): Promise<Prices> => {
|
||||||
|
let conn;
|
||||||
|
let priceRows = [];
|
||||||
|
try {
|
||||||
|
conn = await pool.getConnection();
|
||||||
|
|
||||||
|
let allPrices: Record<number, Price[]> = {};
|
||||||
|
|
||||||
|
// Get newest prices for every product at every vendor
|
||||||
|
|
||||||
|
const rows = await conn.query(
|
||||||
|
'WITH summary AS (\n' +
|
||||||
|
' SELECT p.product_id,\n' +
|
||||||
|
' p.vendor_id,\n' +
|
||||||
|
' p.price_in_cents,\n' +
|
||||||
|
' p.timestamp,\n' +
|
||||||
|
' ROW_NUMBER() OVER(\n' +
|
||||||
|
' PARTITION BY p.product_id, p.vendor_id\n' +
|
||||||
|
' ORDER BY p.timestamp DESC) AS rk\n' +
|
||||||
|
' FROM prices p)\n' +
|
||||||
|
'SELECT s.*\n' +
|
||||||
|
'FROM summary s\n' +
|
||||||
|
'WHERE s.rk = 1');
|
||||||
|
|
||||||
|
// Write returned values to allPrices map with product id as key and a list of prices as value
|
||||||
|
for (let row in rows) {
|
||||||
|
if (row !== 'meta') {
|
||||||
|
if (!allPrices[parseInt(rows[row].product_id)]) {
|
||||||
|
allPrices[parseInt(rows[row].product_id)] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
allPrices[parseInt(rows[row].product_id)].push(rows[row]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all prices to find the products with the biggest difference between amazon and other vendor
|
||||||
|
let deals = [];
|
||||||
|
for (let productId in Object.keys(allPrices)) {
|
||||||
|
if (allPrices[productId]) {
|
||||||
|
let pricesForProd = allPrices[productId];
|
||||||
|
|
||||||
|
// Get amazon price and lowest price from other vendor
|
||||||
|
let amazonPrice = {} as Price;
|
||||||
|
let lowestPrice = {} as Price;
|
||||||
|
pricesForProd.forEach(function(price, priceIndex) {
|
||||||
|
if (price.vendor_id === 1) {
|
||||||
|
amazonPrice = price;
|
||||||
|
} else {
|
||||||
|
if (!lowestPrice.price_in_cents || lowestPrice.price_in_cents > price.price_in_cents) {
|
||||||
|
lowestPrice = price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create deal object and add it to list
|
||||||
|
let deal = {
|
||||||
|
'product_id': lowestPrice.product_id,
|
||||||
|
'vendor_id': lowestPrice.vendor_id,
|
||||||
|
'price_in_cents': lowestPrice.price_in_cents,
|
||||||
|
'timestamp' :lowestPrice.timestamp,
|
||||||
|
'amazonDifference': (amazonPrice.price_in_cents - lowestPrice.price_in_cents),
|
||||||
|
'amazonDifferencePercent': ((1 - (lowestPrice.price_in_cents / amazonPrice.price_in_cents)) * 100),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Push only deals were the amazon price is actually higher
|
||||||
|
if(deal.amazonDifferencePercent > 0) {
|
||||||
|
deals.push(deal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort to have the best deals on the top
|
||||||
|
deals.sort((a, b) => a.amazonDifferencePercent < b.amazonDifferencePercent ? 1 : -1);
|
||||||
|
|
||||||
|
// Return only as many records as requested or the maximum amount of found deals, whatever is less
|
||||||
|
let maxAmt = Math.min(amount, deals.length);
|
||||||
|
|
||||||
|
for (let dealIndex = 0; dealIndex < maxAmt; dealIndex++){
|
||||||
|
//console.log(deals[dealIndex]);
|
||||||
|
priceRows.push(deals[dealIndex] as Price);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
if (conn) {
|
||||||
|
conn.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return priceRows;
|
||||||
|
};
|
||||||
|
|
||||||
// export const create = async (newItem: Product): Promise<void> => {
|
// export const create = async (newItem: Product): Promise<void> => {
|
||||||
// let conn;
|
// let conn;
|
||||||
// try {
|
// try {
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const productsRouter = express.Router();
|
||||||
* Controller Definitions
|
* Controller Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// GET items/
|
// GET products/
|
||||||
|
|
||||||
productsRouter.get('/', async (req: Request, res: Response) => {
|
productsRouter.get('/', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
@ -31,7 +31,7 @@ productsRouter.get('/', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET items/:id
|
// GET products/:id
|
||||||
|
|
||||||
productsRouter.get('/:id', async (req: Request, res: Response) => {
|
productsRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
const id: number = parseInt(req.params.id, 10);
|
const id: number = parseInt(req.params.id, 10);
|
||||||
|
@ -50,7 +50,7 @@ productsRouter.get('/:id', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET items/:name
|
// GET products/search/:term
|
||||||
|
|
||||||
productsRouter.get('/search/:term', async (req: Request, res: Response) => {
|
productsRouter.get('/search/:term', async (req: Request, res: Response) => {
|
||||||
const term: string = req.params.term;
|
const term: string = req.params.term;
|
||||||
|
@ -69,6 +69,8 @@ productsRouter.get('/search/:term', async (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GET products/bestDeals
|
||||||
|
|
||||||
|
|
||||||
// POST items/
|
// POST items/
|
||||||
|
|
||||||
|
|
14
Crawler/Crawler.iml
Normal file
14
Crawler/Crawler.iml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="Python" name="Python">
|
||||||
|
<configuration sdkName="Python 3.9 (venv)" />
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Python 3.9 (venv) interpreter library" level="application" />
|
||||||
|
</component>
|
||||||
|
</module>
|
20
Crawler/Dockerfile
Normal file
20
Crawler/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Base image
|
||||||
|
FROM python
|
||||||
|
|
||||||
|
# Create directories and copy files
|
||||||
|
RUN echo 'Creating directory and copying files'
|
||||||
|
RUN mkdir /crawler
|
||||||
|
ADD . /crawler
|
||||||
|
WORKDIR /crawler
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN echo 'Installing dependencies'
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
RUN echo 'Exposing ports'
|
||||||
|
EXPOSE 22026
|
||||||
|
|
||||||
|
# Start API
|
||||||
|
RUN echo 'Starting API'
|
||||||
|
CMD ["python3", "api.py"]
|
16
Crawler/api.py
Normal file
16
Crawler/api.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from flask import Flask
|
||||||
|
from flask_restful import Resource, Api
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
api = Api(app)
|
||||||
|
|
||||||
|
|
||||||
|
class CrawlerApi(Resource):
|
||||||
|
def get(self):
|
||||||
|
return {'Hallo': 'Betterzon'}
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(CrawlerApi, '/')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=22026)
|
4
Crawler/requirements.txt
Normal file
4
Crawler/requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pymysql
|
||||||
|
flask
|
||||||
|
flask-sqlalchemy
|
||||||
|
flask_restful
|
|
@ -24,9 +24,13 @@
|
||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||||
|
"src/styles.css",
|
||||||
|
"./node_modules/cookieconsent/build/cookieconsent.min.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"./node_modules/cookieconsent/build/cookieconsent.min.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
@ -88,9 +92,13 @@
|
||||||
"src/assets"
|
"src/assets"
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||||
|
"src/styles.css",
|
||||||
|
"./node_modules/cookieconsent/build/cookieconsent.min.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"./node_modules/cookieconsent/build/cookieconsent.min.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
|
|
43857
Frontend/package-lock.json
generated
43857
Frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -12,17 +12,21 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^10.2.3",
|
"@angular/animations": "^10.2.3",
|
||||||
|
"@angular/cdk": "~10.2.7",
|
||||||
"@angular/cli": "^10.2.0",
|
"@angular/cli": "^10.2.0",
|
||||||
"@angular/common": "^10.2.3",
|
"@angular/common": "^10.2.3",
|
||||||
"@angular/compiler": "^10.2.3",
|
"@angular/compiler": "^10.2.3",
|
||||||
"@angular/core": "^10.2.3",
|
"@angular/core": "^10.2.3",
|
||||||
"@angular/forms": "^10.2.3",
|
"@angular/forms": "^10.2.3",
|
||||||
|
"@angular/material": "~10.2.7",
|
||||||
"@angular/platform-browser": "^10.2.3",
|
"@angular/platform-browser": "^10.2.3",
|
||||||
"@angular/platform-browser-dynamic": "^10.2.3",
|
"@angular/platform-browser-dynamic": "^10.2.3",
|
||||||
"@angular/router": "^10.2.3",
|
"@angular/router": "^10.2.3",
|
||||||
"apexcharts": "^3.22.3",
|
"apexcharts": "^3.22.3",
|
||||||
|
"cookieconsent": "^3.1.1",
|
||||||
"ng": "0.0.0",
|
"ng": "0.0.0",
|
||||||
"ng-apexcharts": "^1.5.6",
|
"ng-apexcharts": "^1.5.6",
|
||||||
|
"ngx-cookieconsent": "^2.2.3",
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"tslib": "^2.0.3",
|
"tslib": "^2.0.3",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
|
|
|
@ -1,10 +1,69 @@
|
||||||
import { Component } from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {NgcCookieConsentService, NgcInitializeEvent, NgcNoCookieLawEvent, NgcStatusChangeEvent} from 'ngx-cookieconsent';
|
||||||
|
import {Subscription} from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
title = 'Betterzon';
|
title = 'Betterzon';
|
||||||
|
|
||||||
|
// Cookie popup stuff
|
||||||
|
// Docs on https://www.npmjs.com/package/ngx-cookieconsent
|
||||||
|
private popupOpenSubscription: Subscription;
|
||||||
|
private popupCloseSubscription: Subscription;
|
||||||
|
private initializeSubscription: Subscription;
|
||||||
|
private statusChangeSubscription: Subscription;
|
||||||
|
private revokeChoiceSubscription: Subscription;
|
||||||
|
private noCookieLawSubscription: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private ccService: NgcCookieConsentService
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// subscribe to cookieconsent observables to react to main events
|
||||||
|
this.popupOpenSubscription = this.ccService.popupOpen$.subscribe(
|
||||||
|
() => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
|
||||||
|
this.popupCloseSubscription = this.ccService.popupClose$.subscribe(
|
||||||
|
() => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initializeSubscription = this.ccService.initialize$.subscribe(
|
||||||
|
(event: NgcInitializeEvent) => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
|
||||||
|
this.statusChangeSubscription = this.ccService.statusChange$.subscribe(
|
||||||
|
(event: NgcStatusChangeEvent) => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
|
||||||
|
this.revokeChoiceSubscription = this.ccService.revokeChoice$.subscribe(
|
||||||
|
() => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
|
||||||
|
this.noCookieLawSubscription = this.ccService.noCookieLaw$.subscribe(
|
||||||
|
(event: NgcNoCookieLawEvent) => {
|
||||||
|
// you can use this.ccService.getConfig() to do stuff...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
// unsubscribe to cookieconsent observables to prevent memory leaks
|
||||||
|
this.popupOpenSubscription.unsubscribe();
|
||||||
|
this.popupCloseSubscription.unsubscribe();
|
||||||
|
this.initializeSubscription.unsubscribe();
|
||||||
|
this.statusChangeSubscription.unsubscribe();
|
||||||
|
this.revokeChoiceSubscription.unsubscribe();
|
||||||
|
this.noCookieLawSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,50 @@ 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 {FormsModule} from '@angular/forms';
|
||||||
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
|
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
|
||||||
|
import {MatMenuModule} from '@angular/material/menu';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import {ImprintComponent} from './pages/imprint/imprint.component';
|
||||||
|
import {PrivacyComponent} from './pages/privacy/privacy.component';
|
||||||
|
import {NgcCookieConsentModule, NgcCookieConsentConfig} from 'ngx-cookieconsent';
|
||||||
|
|
||||||
|
// For cookie popup
|
||||||
|
const cookieConfig: NgcCookieConsentConfig = {
|
||||||
|
cookie: {
|
||||||
|
domain: 'betterzon.xyz'
|
||||||
|
},
|
||||||
|
palette: {
|
||||||
|
popup: {
|
||||||
|
background: '#000'
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
background: '#f1d600'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
theme: 'edgeless',
|
||||||
|
type: 'opt-out',
|
||||||
|
layout: 'my-custom-layout',
|
||||||
|
layouts: {
|
||||||
|
'my-custom-layout': '{{messagelink}}{{compliance}}'
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
messagelink: `
|
||||||
|
<span id="cookieconsent:desc" class="cc-message">{{message}}
|
||||||
|
<a aria-label="learn more about cookies" tabindex="0" class="cc-link" href="{{whatAreCookiesHref}}" target="_blank">{{whatAreCookiesLink}}</a>
|
||||||
|
<a aria-label="learn more about our privacy policy" tabindex="1" class="cc-link" href="{{privacyPolicyHref}}" target="_blank">{{privacyPolicyLink}}</a>
|
||||||
|
</span>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
// Custom message
|
||||||
|
// message: 'By using our site, you acknowledge that you have read and understand our ',
|
||||||
|
|
||||||
|
whatAreCookiesLink: 'Learn more',
|
||||||
|
whatAreCookiesHref: 'https://www.cookiesandyou.com/',
|
||||||
|
|
||||||
|
privacyPolicyLink: 'Privacy Policy',
|
||||||
|
privacyPolicyHref: '/datenschutz',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -27,14 +71,19 @@ import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-fo
|
||||||
ProductSearchPageComponent,
|
ProductSearchPageComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NewestPricesListComponent,
|
NewestPricesListComponent,
|
||||||
PageNotFoundPageComponent
|
PageNotFoundPageComponent,
|
||||||
|
ImprintComponent,
|
||||||
|
PrivacyComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
AppRouting,
|
AppRouting,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
NgApexchartsModule,
|
NgApexchartsModule,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
MatMenuModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
NgcCookieConsentModule.forRoot(cookieConfig)
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
|
|
@ -7,11 +7,15 @@ 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';
|
import {PageNotFoundPageComponent} from './pages/page-not-found-page/page-not-found-page.component';
|
||||||
|
import {ImprintComponent} from './pages/imprint/imprint.component';
|
||||||
|
import {PrivacyComponent} from './pages/privacy/privacy.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: 'impressum', component: ImprintComponent},
|
||||||
|
{path: 'datenschutz', component: PrivacyComponent},
|
||||||
{path: '**', component: PageNotFoundPageComponent}
|
{path: '**', component: PageNotFoundPageComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: dimgrey;
|
background-color: dimgrey;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-3d {
|
.icon-3d {
|
||||||
|
@ -13,5 +12,23 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#footer-icons {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imprintSection {
|
||||||
|
position: fixed;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imprintSection a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,16 @@
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class='wrap'>
|
<div class='wrap' id="footer-icons">
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<a href="https://github.com/Mueller-Patrick/Betterzon" class="fa fa-github fa-4x icon-3d"></a>
|
<a href="https://github.com/Mueller-Patrick/Betterzon" class="fa fa-github fa-4x icon-3d"></a>
|
||||||
<a href="https://twitter.com/elonmusk" class="fa fa-twitter fa-4x icon-3d"></a>
|
<a href="https://blog.betterzon.xyz/" class="fa fa-info fa-4x icon-3d"></a>
|
||||||
<a href="https://www.facebook.com/TheElonmusk/" class="fa fa-facebook fa-4x icon-3d"></a>
|
<a href="https://github.com/Mueller-Patrick/Betterzon/wiki" class="fa fa-wikipedia-w fa-4x icon-3d"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class = "blocks" id="copyright">© COPYRIGHT 2020 </div>
|
<div class = "blocks" id="copyright">© COPYRIGHT 2020</div>
|
||||||
|
</div>
|
||||||
|
<div id="imprintSection">
|
||||||
|
<a href="/impressum" >Imprint</a><br>
|
||||||
|
<a href="/datenschutz">Privacy Policy</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-footer',
|
selector: 'app-footer',
|
||||||
|
@ -7,9 +8,16 @@ import { Component, OnInit } from '@angular/core';
|
||||||
})
|
})
|
||||||
export class FooterComponent implements OnInit {
|
export class FooterComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
navigateImprint(): void {
|
||||||
|
this.router.navigate([('/impressum/')]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
margin-left: 50%;
|
margin-left: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.searchBox {
|
.searchBox {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -36,6 +40,10 @@
|
||||||
margin-left: 10%;
|
margin-left: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.profileIcon:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-3d {
|
.icon-3d {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -8,7 +8,13 @@
|
||||||
<input *ngIf="showSearch===true" type="text" [(ngModel)]="searchInput" placeholder="Search" (keyup.enter)="startedSearch()">
|
<input *ngIf="showSearch===true" type="text" [(ngModel)]="searchInput" placeholder="Search" (keyup.enter)="startedSearch()">
|
||||||
</div>
|
</div>
|
||||||
<div class="profileIcon">
|
<div class="profileIcon">
|
||||||
Profile
|
<button mat-button [matMenuTriggerFor]="menu">Menu</button>
|
||||||
|
<mat-menu #menu="matMenu">
|
||||||
|
<button mat-menu-item>Option 1</button>
|
||||||
|
<button mat-menu-item>Option 2</button>
|
||||||
|
<button mat-menu-item>Option 3</button>
|
||||||
|
<button mat-menu-item>Option 4</button>
|
||||||
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -4,8 +4,9 @@ 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;
|
searchInput: string;
|
||||||
@Input() showSearch: boolean;
|
@Input() showSearch: boolean;
|
||||||
|
|
|
@ -81,3 +81,8 @@
|
||||||
margin-top: .5em;
|
margin-top: .5em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.priceAlarm:hover {
|
||||||
|
background-color: lightgray;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
|
@ -53,3 +53,8 @@
|
||||||
.productDescription {
|
.productDescription {
|
||||||
grid-area: description;
|
grid-area: description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.productItem:hover {
|
||||||
|
background-color: lightgray;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
4
Frontend/src/app/pages/imprint/imprint.component.css
Normal file
4
Frontend/src/app/pages/imprint/imprint.component.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#imprint {
|
||||||
|
padding-left: 5em;
|
||||||
|
padding-right: 5em;
|
||||||
|
}
|
59
Frontend/src/app/pages/imprint/imprint.component.html
Normal file
59
Frontend/src/app/pages/imprint/imprint.component.html
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<app-header [showSearch]="true"></app-header>
|
||||||
|
<div id="imprint">
|
||||||
|
<h1>Impressum</h1>
|
||||||
|
|
||||||
|
<h2>Angaben gemäß § 5 TMG</h2>
|
||||||
|
<p>
|
||||||
|
Patrick Müller<br/>
|
||||||
|
Schachtstraße 8<br/>
|
||||||
|
75031 Eppingen
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Kontakt</h2>
|
||||||
|
<p>
|
||||||
|
E-Mail: betterzon-contact@mueller-patrick.tech
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Haftung für Inhalte</h3>
|
||||||
|
<p>
|
||||||
|
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene
|
||||||
|
Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als
|
||||||
|
Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen
|
||||||
|
oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben
|
||||||
|
hiervon unberührt.
|
||||||
|
Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich.
|
||||||
|
Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Haftung für Links</h3>
|
||||||
|
<p>
|
||||||
|
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen
|
||||||
|
Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für
|
||||||
|
die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die
|
||||||
|
verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft.
|
||||||
|
Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer
|
||||||
|
Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend
|
||||||
|
entfernen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Urheberrecht</h3>
|
||||||
|
<p>
|
||||||
|
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht.
|
||||||
|
Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des
|
||||||
|
Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und
|
||||||
|
Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet.
|
||||||
|
Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung
|
||||||
|
aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir
|
||||||
|
derartige Inhalte umgehend entfernen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<app-footer></app-footer>
|
25
Frontend/src/app/pages/imprint/imprint.component.spec.ts
Normal file
25
Frontend/src/app/pages/imprint/imprint.component.spec.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ImprintComponent } from './imprint.component';
|
||||||
|
|
||||||
|
describe('ImprintComponent', () => {
|
||||||
|
let component: ImprintComponent;
|
||||||
|
let fixture: ComponentFixture<ImprintComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ ImprintComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ImprintComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
15
Frontend/src/app/pages/imprint/imprint.component.ts
Normal file
15
Frontend/src/app/pages/imprint/imprint.component.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-imprint',
|
||||||
|
templateUrl: './imprint.component.html',
|
||||||
|
styleUrls: ['./imprint.component.css']
|
||||||
|
})
|
||||||
|
export class ImprintComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
Frontend/src/app/pages/privacy/privacy.component.css
Normal file
4
Frontend/src/app/pages/privacy/privacy.component.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#privacy {
|
||||||
|
padding-left: 5em;
|
||||||
|
padding-right: 5em;
|
||||||
|
}
|
408
Frontend/src/app/pages/privacy/privacy.component.html
Normal file
408
Frontend/src/app/pages/privacy/privacy.component.html
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
<app-header [showSearch]="true"></app-header>
|
||||||
|
<div id="privacy">
|
||||||
|
<h1>Datenschutz­erklärung</h1>
|
||||||
|
<h2>1. Datenschutz auf einen Blick</h2>
|
||||||
|
<h3>Allgemeine Hinweise</h3>
|
||||||
|
<p>
|
||||||
|
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren
|
||||||
|
personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen
|
||||||
|
Sie persönlich identifiziert werden können. Ausführliche Informationen zum Thema Datenschutz
|
||||||
|
entnehmen Sie unserer unter diesem Text aufgeführten Datenschutzerklärung.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Datenerfassung auf dieser Website</h3>
|
||||||
|
<h4>Wer ist verantwortlich für die Datenerfassung auf dieser Website?</h4>
|
||||||
|
<p>
|
||||||
|
Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten können
|
||||||
|
Sie dem Abschnitt „Hinweis zur Verantwortlichen Stelle“ in dieser Datenschutzerklärung
|
||||||
|
entnehmen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Wie erfassen wir Ihre Daten?</h4>
|
||||||
|
<p>
|
||||||
|
Ihre Daten werden zum einen dadurch erhoben, dass Sie uns
|
||||||
|
diese mitteilen. Hierbei kann es sich z. B. um Daten handeln, die Sie in ein Kontaktformular eingeben.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch der Website durch unsere IT-Systeme
|
||||||
|
erfasst. Das sind vor allem technische Daten (z. B. Internetbrowser, Betriebssystem oder Uhrzeit des
|
||||||
|
Seitenaufrufs). Die Erfassung dieser Daten erfolgt automatisch, sobald Sie diese Website betreten.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Wofür nutzen wir Ihre Daten?</h4>
|
||||||
|
<p>
|
||||||
|
Ein Teil der Daten wird erhoben, um eine fehlerfreie Bereitstellung der Website zu
|
||||||
|
gewährleisten. Andere Daten können zur Analyse Ihres Nutzerverhaltens verwendet werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Welche Rechte haben Sie bezüglich Ihrer Daten?</h4>
|
||||||
|
<p>
|
||||||
|
Sie haben jederzeit das Recht, unentgeltlich Auskunft über
|
||||||
|
Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem
|
||||||
|
ein Recht, die Berichtigung oder Löschung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur
|
||||||
|
Datenverarbeitung erteilt haben, können Sie diese Einwilligung jederzeit für die Zukunft widerrufen. Außerdem
|
||||||
|
haben Sie das Recht, unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen
|
||||||
|
Daten zu verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde
|
||||||
|
zu.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Hierzu sowie zu weiteren Fragen zum Thema Datenschutz können Sie sich jederzeit an uns wenden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>2. Hosting und Content Delivery Networks (CDN)</h2>
|
||||||
|
<h3>Externes Hosting</h3>
|
||||||
|
<p>
|
||||||
|
Diese Website wird bei einem externen Dienstleister gehostet (Hoster). Die
|
||||||
|
personenbezogenen Daten, die auf dieser Website erfasst werden, werden auf den Servern des Hosters gespeichert.
|
||||||
|
Hierbei kann es sich v. a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Vertragsdaten,
|
||||||
|
Kontaktdaten, Namen, Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Der Einsatz des Hosters erfolgt zum Zwecke der Vertragserfüllung gegenüber unseren potenziellen und
|
||||||
|
bestehenden Kunden (Art. 6 Abs. 1 lit. b DSGVO) und im Interesse einer sicheren, schnellen und effizienten
|
||||||
|
Bereitstellung unseres Online-Angebots durch einen professionellen Anbieter (Art. 6 Abs. 1 lit. f DSGVO).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Unser Hoster wird Ihre Daten nur insoweit verarbeiten, wie dies zur Erfüllung seiner Leistungspflichten
|
||||||
|
erforderlich ist und unsere Weisungen in Bezug auf diese Daten befolgen.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wir setzen folgenden Hoster ein:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
netcup GmbH<br/>
|
||||||
|
Daimlerstraße 25<br/>
|
||||||
|
76185 Karlsruhe
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>Abschluss eines Vertrages über Auftragsverarbeitung</h4>
|
||||||
|
<p>
|
||||||
|
Um die datenschutzkonforme Verarbeitung zu gewährleisten,
|
||||||
|
haben wir einen Vertrag über Auftragsverarbeitung mit unserem Hoster geschlossen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>3. Allgemeine Hinweise und Pflicht­informationen</h2>
|
||||||
|
<h3>Datenschutz</h3>
|
||||||
|
<p>
|
||||||
|
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir
|
||||||
|
behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie
|
||||||
|
dieser Datenschutzerklärung.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten
|
||||||
|
erhoben. Personenbezogene Daten sind Daten, mit denen Sie persönlich identifiziert werden können. Die
|
||||||
|
vorliegende Datenschutzerklärung erläutert, welche Daten wir erheben und wofür wir sie nutzen. Sie
|
||||||
|
erläutert auch, wie und zu welchem Zweck das geschieht.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wir weisen darauf hin, dass die Datenübertragung
|
||||||
|
im Internet (z. B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser
|
||||||
|
Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Hinweis zur verantwortlichen Stelle</h3>
|
||||||
|
<p>
|
||||||
|
Die verantwortliche Stelle für die Datenverarbeitung auf dieser
|
||||||
|
Website ist:
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Patrick Müller<br/>
|
||||||
|
Schachtstraße 8<br/>
|
||||||
|
75031 Eppingen
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
E-Mail: betterzon-privacy@mueller-patrick.tech
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Verantwortliche Stelle ist die natürliche oder juristische Person, die allein oder gemeinsam mit anderen über
|
||||||
|
die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z. B. Namen, E-Mail-Adressen o. Ä.)
|
||||||
|
entscheidet.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Speicherdauer</h3>
|
||||||
|
<p>
|
||||||
|
Soweit innerhalb dieser Datenschutzerklärung keine speziellere Speicherdauer genannt
|
||||||
|
wurde, verbleiben Ihre personenbezogenen Daten bei uns, bis der Zweck für die Datenverarbeitung entfällt.
|
||||||
|
Wenn Sie ein berechtigtes Löschersuchen geltend machen oder eine Einwilligung zur Datenverarbeitung widerrufen,
|
||||||
|
werden Ihre Daten gelöscht, sofern wir keine anderen rechtlich zulässigen Gründe für die
|
||||||
|
Speicherung Ihrer personenbezogenen Daten haben (z.B. steuer- oder handelsrechtliche Aufbewahrungsfristen); im
|
||||||
|
letztgenannten Fall erfolgt die Löschung nach Fortfall dieser Gründe.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Hinweis zur Datenweitergabe in die USA und sonstige Drittstaaten</h3>
|
||||||
|
<p>
|
||||||
|
Auf unserer Website sind unter anderem
|
||||||
|
Tools von Unternehmen mit Sitz in den USA oder sonstigen datenschutzrechtlich nicht sicheren Drittstaaten
|
||||||
|
eingebunden. Wenn diese Tools aktiv sind, können Ihre personenbezogene Daten in diese Drittstaaten übertragen
|
||||||
|
und dort verarbeitet werden. Wir weisen darauf hin, dass in diesen Ländern kein mit der EU vergleichbares
|
||||||
|
Datenschutzniveau garantiert werden kann. Beispielsweise sind US-Unternehmen dazu verpflichtet, personenbezogene
|
||||||
|
Daten an Sicherheitsbehörden herauszugeben, ohne dass Sie als Betroffener hiergegen gerichtlich vorgehen könnten.
|
||||||
|
Es kann daher nicht ausgeschlossen werden, dass US-Behörden (z.B. Geheimdienste) Ihre auf US-Servern
|
||||||
|
befindlichen Daten zu Überwachungszwecken verarbeiten, auswerten und dauerhaft speichern. Wir haben auf diese
|
||||||
|
Verarbeitungstätigkeiten keinen Einfluss.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Widerruf Ihrer Einwilligung zur Datenverarbeitung</h3>
|
||||||
|
<p>
|
||||||
|
Viele Datenverarbeitungsvorgänge sind nur mit Ihrer ausdrücklichen Einwilligung möglich. Sie können
|
||||||
|
eine bereits erteilte Einwilligung jederzeit widerrufen. Die Rechtmäßigkeit der bis zum Widerruf
|
||||||
|
erfolgten Datenverarbeitung bleibt vom Widerruf unberührt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21 DSGVO)</h3>
|
||||||
|
<p>
|
||||||
|
WENN DIE DATENVERARBEITUNG AUF GRUNDLAGE VON ART. 6 ABS. 1 LIT. E ODER F DSGVO ERFOLGT, HABEN SIE JEDERZEIT DAS
|
||||||
|
RECHT, AUS GRÜNDEN, DIE SICH AUS IHRER BESONDEREN SITUATION ERGEBEN, GEGEN DIE VERARBEITUNG IHRER
|
||||||
|
PERSONENBEZOGENEN DATEN WIDERSPRUCH EINZULEGEN; DIES GILT AUCH FÜR EIN AUF DIESE BESTIMMUNGEN GESTÜTZTES
|
||||||
|
PROFILING. DIE JEWEILIGE RECHTSGRUNDLAGE, AUF DENEN EINE VERARBEITUNG BERUHT, ENTNEHMEN SIE DIESER DATENSCHUTZERKLÄRUNG.
|
||||||
|
WENN SIE WIDERSPRUCH EINLEGEN, WERDEN WIR IHRE BETROFFENEN PERSONENBEZOGENEN DATEN NICHT MEHR VERARBEITEN, ES SEI
|
||||||
|
DENN, WIR KÖNNEN ZWINGENDE SCHUTZWÜRDIGE GRÜNDE FÜR DIE VERARBEITUNG NACHWEISEN, DIE IHRE
|
||||||
|
INTERESSEN, RECHTE UND FREIHEITEN ÜBERWIEGEN ODER DIE VERARBEITUNG DIENT DER GELTENDMACHUNG, AUSÜBUNG ODER
|
||||||
|
VERTEIDIGUNG VON RECHTSANSPRÜCHEN (WIDERSPRUCH NACH ART. 21 ABS. 1 DSGVO).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
WERDEN IHRE PERSONENBEZOGENEN
|
||||||
|
DATEN VERARBEITET, UM DIREKTWERBUNG ZU BETREIBEN, SO HABEN SIE DAS RECHT, JEDERZEIT WIDERSPRUCH GEGEN DIE
|
||||||
|
VERARBEITUNG SIE BETREFFENDER PERSONENBEZOGENER DATEN ZUM ZWECKE DERARTIGER WERBUNG EINZULEGEN; DIES GILT AUCH FÜR
|
||||||
|
DAS PROFILING, SOWEIT ES MIT SOLCHER DIREKTWERBUNG IN VERBINDUNG STEHT. WENN SIE WIDERSPRECHEN, WERDEN IHRE
|
||||||
|
PERSONENBEZOGENEN DATEN ANSCHLIESSEND NICHT MEHR ZUM ZWECKE DER DIREKTWERBUNG VERWENDET (WIDERSPRUCH NACH ART. 21
|
||||||
|
ABS. 2 DSGVO).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Beschwerde­recht bei der zuständigen Aufsichts­behörde</h3>
|
||||||
|
<p>
|
||||||
|
Im Falle von Verstößen
|
||||||
|
gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer Aufsichtsbehörde, insbesondere in dem
|
||||||
|
Mitgliedstaat ihres gewöhnlichen Aufenthalts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen Verstoßes
|
||||||
|
zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher
|
||||||
|
Rechtsbehelfe.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Recht auf Daten­übertrag­barkeit</h3>
|
||||||
|
<p>
|
||||||
|
Sie haben das Recht, Daten, die wir auf Grundlage Ihrer
|
||||||
|
Einwilligung oder in Erfüllung eines Vertrags automatisiert verarbeiten, an sich oder an einen Dritten in einem
|
||||||
|
gängigen, maschinenlesbaren Format aushändigen zu lassen. Sofern Sie die direkte Übertragung der
|
||||||
|
Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>SSL- bzw. TLS-Verschlüsselung</h3>
|
||||||
|
<p>
|
||||||
|
Diese Seite nutzt aus Sicherheitsgründen und zum Schutz der Übertragung
|
||||||
|
vertraulicher Inhalte, wie zum Beispiel Bestellungen oder Anfragen, die Sie an uns als Seitenbetreiber senden, eine
|
||||||
|
SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen Sie daran, dass die Adresszeile des
|
||||||
|
Browsers von „http://“ auf „https://“ wechselt und an dem Schloss-Symbol in Ihrer
|
||||||
|
Browserzeile.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wenn die SSL- bzw. TLS-Verschlüsselung aktiviert ist, können die Daten, die Sie an
|
||||||
|
uns übermitteln, nicht von Dritten mitgelesen werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Auskunft, Löschung und Berichtigung</h3>
|
||||||
|
<p>
|
||||||
|
Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen
|
||||||
|
jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen Daten, deren Herkunft
|
||||||
|
und Empfänger und den Zweck der Datenverarbeitung und ggf. ein Recht auf Berichtigung oder Löschung dieser
|
||||||
|
Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene Daten können Sie sich jederzeit an uns
|
||||||
|
wenden.
|
||||||
|
</p>
|
||||||
|
<h3>Recht auf Einschränkung der Verarbeitung</h3>
|
||||||
|
<p>
|
||||||
|
Sie haben das Recht, die Einschränkung der Verarbeitung
|
||||||
|
Ihrer personenbezogenen Daten zu verlangen. Hierzu können Sie sich jederzeit an uns wenden. Das Recht auf
|
||||||
|
Einschränkung der Verarbeitung besteht in folgenden Fällen:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir in
|
||||||
|
der Regel Zeit, um dies zu überprüfen. Für die Dauer der Prüfung haben Sie das Recht, die
|
||||||
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können Sie
|
||||||
|
statt der Löschung die Einschränkung der Datenverarbeitung verlangen.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Wenn wir Ihre personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung, Verteidigung
|
||||||
|
oder Geltendmachung von Rechtsansprüchen benötigen, haben Sie das Recht, statt der Löschung die
|
||||||
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren und
|
||||||
|
unseren Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen überwiegen, haben
|
||||||
|
Sie das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschränkt haben, dürfen diese Daten
|
||||||
|
– von ihrer Speicherung abgesehen – nur mit Ihrer Einwilligung oder zur Geltendmachung, Ausübung
|
||||||
|
oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen natürlichen oder
|
||||||
|
juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der Europäischen Union
|
||||||
|
oder eines Mitgliedstaats verarbeitet werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>4. Datenerfassung auf dieser Website</h2>
|
||||||
|
<h3>Cookies</h3>
|
||||||
|
<p>
|
||||||
|
Unsere Internetseiten verwenden so genannte „Cookies“. Cookies sind kleine Textdateien
|
||||||
|
und richten auf Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer
|
||||||
|
einer Sitzung (Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert.
|
||||||
|
Session-Cookies werden nach Ende Ihres Besuchs automatisch gelöscht. Permanente Cookies bleiben auf Ihrem
|
||||||
|
Endgerät gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch Ihren
|
||||||
|
Webbrowser erfolgt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Teilweise können auch Cookies von Drittunternehmen auf Ihrem Endgerät
|
||||||
|
gespeichert werden, wenn Sie unsere Seite betreten (Third-Party-Cookies). Diese ermöglichen uns oder Ihnen die
|
||||||
|
Nutzung bestimmter Dienstleistungen des Drittunternehmens (z.B. Cookies zur Abwicklung von
|
||||||
|
Zahlungsdienstleistungen).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Cookies haben verschiedene Funktionen. Zahlreiche Cookies sind technisch
|
||||||
|
notwendig, da bestimmte Websitefunktionen ohne diese nicht funktionieren würden (z.B. die Warenkorbfunktion
|
||||||
|
oder die Anzeige von Videos). Andere Cookies dienen dazu, das Nutzerverhalten auszuwerten oder Werbung
|
||||||
|
anzuzeigen.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Cookies, die zur Durchführung des elektronischen Kommunikationsvorgangs (notwendige Cookies)
|
||||||
|
oder zur Bereitstellung bestimmter, von Ihnen erwünschter Funktionen (funktionale Cookies, z. B. für die
|
||||||
|
Warenkorbfunktion) oder zur Optimierung der Website (z.B. Cookies zur Messung des Webpublikums) erforderlich sind,
|
||||||
|
werden auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO gespeichert, sofern keine andere Rechtsgrundlage angegeben wird.
|
||||||
|
Der Websitebetreiber hat ein berechtigtes Interesse an der Speicherung von Cookies zur technisch fehlerfreien und
|
||||||
|
optimierten Bereitstellung seiner Dienste. Sofern eine Einwilligung zur Speicherung von Cookies abgefragt wurde,
|
||||||
|
erfolgt die Speicherung der betreffenden Cookies ausschließlich auf Grundlage dieser Einwilligung (Art. 6 Abs.
|
||||||
|
1 lit. a DSGVO); die Einwilligung ist jederzeit widerrufbar.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Sie können Ihren Browser so einstellen,
|
||||||
|
dass Sie über das Setzen von Cookies informiert werden und Cookies nur im Einzelfall erlauben, die Annahme von
|
||||||
|
Cookies für bestimmte Fälle oder generell ausschließen sowie das automatische Löschen der
|
||||||
|
Cookies beim Schließen des Browsers aktivieren. Bei der Deaktivierung von Cookies kann die Funktionalität
|
||||||
|
dieser Website eingeschränkt sein.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Soweit Cookies von Drittunternehmen oder zu Analysezwecken eingesetzt
|
||||||
|
werden, werden wir Sie hierüber im Rahmen dieser Datenschutzerklärung gesondert informieren und ggf. eine
|
||||||
|
Einwilligung abfragen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Server-Log-Dateien</h3>
|
||||||
|
<p>
|
||||||
|
Der Provider der Seiten erhebt und speichert automatisch Informationen in so genannten
|
||||||
|
Server-Log-Dateien, die Ihr Browser automatisch an uns übermittelt. Dies sind:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Browsertyp und Browserversion</li>
|
||||||
|
<li>verwendetes Betriebssystem</li>
|
||||||
|
<li>Referrer URL</li>
|
||||||
|
<li>Hostname des zugreifenden Rechners</li>
|
||||||
|
<li>Uhrzeit der Serveranfrage</li>
|
||||||
|
<li>IP-Adresse</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Eine Zusammenführung dieser Daten mit anderen Datenquellen wird nicht vorgenommen.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die Erfassung
|
||||||
|
dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes
|
||||||
|
Interesse an der technisch fehlerfreien Darstellung und der Optimierung seiner Website – hierzu müssen
|
||||||
|
die Server-Log-Files erfasst werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Kontaktformular</h3>
|
||||||
|
<p>
|
||||||
|
Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem
|
||||||
|
Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und für
|
||||||
|
den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erfüllung
|
||||||
|
eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen erforderlich ist. In
|
||||||
|
allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse an der effektiven
|
||||||
|
Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung (Art. 6 Abs. 1
|
||||||
|
lit. a DSGVO) sofern diese abgefragt wurde.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die von Ihnen im Kontaktformular eingegebenen Daten verbleiben
|
||||||
|
bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen oder der Zweck für
|
||||||
|
die Datenspeicherung entfällt (z. B. nach abgeschlossener Bearbeitung Ihrer Anfrage). Zwingende
|
||||||
|
gesetzliche Bestimmungen – insbesondere Aufbewahrungsfristen – bleiben unberührt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Anfrage per E-Mail, Telefon oder Telefax</h3>
|
||||||
|
<p>
|
||||||
|
Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird
|
||||||
|
Ihre Anfrage inklusive aller daraus hervorgehenden personenbezogenen Daten (Name, Anfrage) zum Zwecke der
|
||||||
|
Bearbeitung Ihres Anliegens bei uns gespeichert und verarbeitet. Diese Daten geben wir nicht ohne Ihre Einwilligung
|
||||||
|
weiter.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre
|
||||||
|
Anfrage mit der Erfüllung eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen
|
||||||
|
erforderlich ist. In allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse an
|
||||||
|
der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung
|
||||||
|
(Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Die von Ihnen an uns per Kontaktanfragen übersandten
|
||||||
|
Daten verbleiben bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen
|
||||||
|
oder der Zweck für die Datenspeicherung entfällt (z. B. nach abgeschlossener Bearbeitung Ihres
|
||||||
|
Anliegens). Zwingende gesetzliche Bestimmungen – insbesondere gesetzliche Aufbewahrungsfristen – bleiben
|
||||||
|
unberührt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>5. Plugins und Tools</h2>
|
||||||
|
<h3>Google Web Fonts</h3>
|
||||||
|
<p>
|
||||||
|
Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten so genannte Web Fonts, die
|
||||||
|
von Google bereitgestellt werden. Beim Aufruf einer Seite lädt Ihr Browser die benötigten Web Fonts in
|
||||||
|
ihren Browsercache, um Texte und Schriftarten korrekt anzuzeigen.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Zu diesem Zweck muss der von Ihnen
|
||||||
|
verwendete Browser Verbindung zu den Servern von Google aufnehmen. Hierdurch erlangt Google Kenntnis darüber,
|
||||||
|
dass über Ihre IP-Adresse diese Website aufgerufen wurde. Die Nutzung von Google WebFonts erfolgt auf Grundlage
|
||||||
|
von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes Interesse an der einheitlichen Darstellung
|
||||||
|
des Schriftbildes auf seiner Website. Sofern eine entsprechende Einwilligung abgefragt wurde (z. B. eine
|
||||||
|
Einwilligung zur Speicherung von Cookies), erfolgt die Verarbeitung ausschließlich auf Grundlage von Art. 6
|
||||||
|
Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit widerrufbar.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wenn Ihr Browser Web Fonts nicht unterstützt,
|
||||||
|
wird eine Standardschrift von Ihrem Computer genutzt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Weitere Informationen zu Google Web Fonts finden Sie
|
||||||
|
unter <a href="https://developers.google.com/fonts/faq" target="_blank" rel="noopener noreferrer">https://developers.google.com/fonts/faq</a>
|
||||||
|
und in der Datenschutzerklärung von Google: <a href="https://policies.google.com/privacy?hl=de" target="_blank"
|
||||||
|
rel="noopener noreferrer">https://policies.google.com/privacy?hl=de</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Font Awesome</h3>
|
||||||
|
<p>
|
||||||
|
Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten und Symbolen Font Awesome.
|
||||||
|
Anbieter ist die Fonticons, Inc., 6 Porter Road Apartment 3R, Cambridge, Massachusetts, USA.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Beim Aufruf
|
||||||
|
einer Seite lädt Ihr Browser die benötigten Fonts in ihren Browsercache, um Texte, Schriftarten und
|
||||||
|
Symbole korrekt anzuzeigen. Zu diesem Zweck muss der von Ihnen verwendete Browser Verbindung zu den Servern von Font
|
||||||
|
Awesome aufnehmen. Hierdurch erlangt Font Awesome Kenntnis darüber, dass über Ihre IP-Adresse diese
|
||||||
|
Website aufgerufen wurde. Die Nutzung von Font Awesome erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Wir
|
||||||
|
haben ein berechtigtes Interesse an der einheitlichen Darstellung des Schriftbildes auf unserer Website. Sofern eine
|
||||||
|
entsprechende Einwilligung abgefragt wurde (z. B. eine Einwilligung zur Speicherung von Cookies), erfolgt die
|
||||||
|
Verarbeitung ausschließlich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit
|
||||||
|
widerrufbar.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wenn Ihr Browser Font Awesome nicht unterstützt, wird eine Standardschrift von Ihrem
|
||||||
|
Computer genutzt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Weitere Informationen zu Font Awesome finden Sie und in der Datenschutzerklärung
|
||||||
|
von Font Awesome unter: <a href="https://fontawesome.com/privacy" target="_blank" rel="noopener noreferrer">https://fontawesome.com/privacy</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<app-footer></app-footer>
|
25
Frontend/src/app/pages/privacy/privacy.component.spec.ts
Normal file
25
Frontend/src/app/pages/privacy/privacy.component.spec.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PrivacyComponent } from './privacy.component';
|
||||||
|
|
||||||
|
describe('PrivacyComponent', () => {
|
||||||
|
let component: PrivacyComponent;
|
||||||
|
let fixture: ComponentFixture<PrivacyComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PrivacyComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PrivacyComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
15
Frontend/src/app/pages/privacy/privacy.component.ts
Normal file
15
Frontend/src/app/pages/privacy/privacy.component.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-privacy',
|
||||||
|
templateUrl: './privacy.component.html',
|
||||||
|
styleUrls: ['./privacy.component.css']
|
||||||
|
})
|
||||||
|
export class PrivacyComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Betterzon</title>
|
<title>Betterzon</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
</head>
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||||
<body>
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<app-root></app-root>
|
<script src="node_modules/cookieconsent/build/cookieconsent.min.js"></script>
|
||||||
</body>
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -4,3 +4,6 @@ body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
margin-bottom: 10em;
|
margin-bottom: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body { height: 100%; }
|
||||||
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||||
|
|
|
@ -4,7 +4,8 @@ Blog: https://blog.betterzon.xyz<br>
|
||||||
Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
||||||
|
|
||||||
# Project Status
|
# Project Status
|
||||||
[![build status](https://github.com/Mueller-Patrick/Betterzon/workflows/Build/badge.svg)](https://github.com/Mueller-Patrick/Betterzon/actions?query=branch%3Amaster)
|
![Latest Commit Build Status](https://ci.betterzon.xyz/job/Verify_Build_on_PR/badge/icon?style=flat-square&subject=Latest%20Commit)
|
||||||
|
![Deployment Status](https://ci.betterzon.xyz/job/GitHub%20Deployment/badge/icon?style=flat-square&subject=Deployment&status=Success)
|
||||||
# How to build
|
<br>
|
||||||
Run ```ng build --prod``` to build a production version of the project
|
[![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)
|
||||||
|
[![Blog Status](https://img.shields.io/website?label=blog.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fblog.betterzon.xyz)](https://blog.betterzon.xyz)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user