mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2026-05-04 18:57:59 +00:00
Compare commits
63 Commits
aae931099b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b57ebfb53 | |||
| ddebe78f15 | |||
| 086e0d41b3 | |||
| 2bc97503de | |||
| 8049fd9987 | |||
| b839994de0 | |||
| 91716b8147 | |||
| 5be6d9bd4f | |||
| 6e0f1e7659 | |||
| 4a7ef6d637 | |||
| c3c0fa73b9 | |||
| b1db97af87 | |||
| 730e1aada0 | |||
| deec125242 | |||
| d8638cab4b | |||
| cfda636f07 | |||
| 61a0125229 | |||
| 0bc02ee9ea | |||
| 7ac73204df | |||
| 5c02314180 | |||
| 52e38461a4 | |||
| 45b5e04442 | |||
| a792c43e24 | |||
| 78e2de6545 | |||
| a6a5b58e25 | |||
| 012de346e8 | |||
| f28b301a28 | |||
| af85f8ff7b | |||
| 45acbfd9a2 | |||
| fe2c064e30 | |||
| 9821c004ec | |||
| b748b9492a | |||
| 969ac6feaf | |||
| be534551ba | |||
| c88efc5484 | |||
| 8eba80f7d4 | |||
| 6f33186d7e | |||
| 841502f9d1 | |||
| c90949de47 | |||
| 7f43d27a79 | |||
| d83fcdf693 | |||
| ce92abdb40 | |||
| ce083d8abb | |||
| 0cd1213c40 | |||
| d9ad99cb72 | |||
| 1fd115c2a2 | |||
| 92de923fad | |||
| bc437363e5 | |||
| 0118ac6861 | |||
| 83f6018f7e | |||
| bf56d2b509 | |||
| ad091954c1 | |||
| b062e14c9a | |||
| f1dcaef351 | |||
| 391a4b5e4b | |||
| cf300cb1b7 | |||
| c35097a9a2 | |||
| 62795fd3f8 | |||
| 4a0ba40da6 | |||
| f4d1e93a7f | |||
| daef6ec208 | |||
| f2adb1e375 | |||
| 68e9d75e2d |
@@ -76,7 +76,9 @@ contactpersonsRouter.post('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const vendor_id = req.body.vendor_id;
|
const vendor_id = req.body.vendor_id;
|
||||||
@@ -104,7 +106,9 @@ contactpersonsRouter.put('/:id', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const contact_person_id = parseInt(req.params.id, 10);
|
const contact_person_id = parseInt(req.params.id, 10);
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ crawlingstatusRouter.get('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
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);
|
||||||
|
|
||||||
if (!user.is_admin) {
|
if (!user.is_admin) {
|
||||||
res.status(403).send({});
|
res.status(403).send({});
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ favoriteshopsRouter.get('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
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 priceAlarms = await FavoriteShopsService.getFavoriteShops(user.user_id);
|
const priceAlarms = await FavoriteShopsService.getFavoriteShops(user.user_id);
|
||||||
|
|
||||||
@@ -40,7 +42,9 @@ favoriteshopsRouter.post('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get info for price alarm creation
|
// Get info for price alarm creation
|
||||||
const vendor_id = req.body.vendor_id;
|
const vendor_id = req.body.vendor_id;
|
||||||
@@ -72,7 +76,9 @@ favoriteshopsRouter.delete('/:id', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
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);
|
||||||
|
|
||||||
// Get info for price alarm creation
|
// Get info for price alarm creation
|
||||||
const favorite_id = parseInt(req.params.id, 10);
|
const favorite_id = parseInt(req.params.id, 10);
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ pricealarmsRouter.get('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
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 priceAlarms = await PriceAlarmsService.getPriceAlarms(user.user_id);
|
const priceAlarms = await PriceAlarmsService.getPriceAlarms(user.user_id);
|
||||||
|
|
||||||
@@ -40,7 +42,9 @@ pricealarmsRouter.post('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get info for price alarm creation
|
// Get info for price alarm creation
|
||||||
const product_id = req.body.product_id;
|
const product_id = req.body.product_id;
|
||||||
@@ -73,7 +77,9 @@ pricealarmsRouter.put('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get info for price alarm creation
|
// Get info for price alarm creation
|
||||||
const alarm_id = req.body.alarm_id;
|
const alarm_id = req.body.alarm_id;
|
||||||
@@ -100,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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ pricesRouter.post('/', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const vendor_id = req.body.vendor_id;
|
const vendor_id = req.body.vendor_id;
|
||||||
|
|||||||
@@ -282,11 +282,11 @@ export const getBestDeals = async (amount: number): Promise<Prices> => {
|
|||||||
'price_in_cents': lowestPrice.price_in_cents,
|
'price_in_cents': lowestPrice.price_in_cents,
|
||||||
'timestamp': lowestPrice.timestamp,
|
'timestamp': lowestPrice.timestamp,
|
||||||
'amazonDifference': (amazonPrice.price_in_cents - lowestPrice.price_in_cents),
|
'amazonDifference': (amazonPrice.price_in_cents - lowestPrice.price_in_cents),
|
||||||
'amazonDifferencePercent': ((1 - (lowestPrice.price_in_cents / amazonPrice.price_in_cents)) * 100),
|
'amazonDifferencePercent': ((amazonPrice.price_in_cents / lowestPrice.price_in_cents) * 100),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push only deals were the amazon price is actually higher
|
// Push only deals were the amazon price is actually higher
|
||||||
if (deal.amazonDifferencePercent > 0) {
|
if (deal.amazonDifferencePercent > 0 && deal.amazonDifference > 0) {
|
||||||
deals.push(deal as Deal);
|
deals.push(deal as Deal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ usersRouter.post('/register', async (req: Request, res: Response) => {
|
|||||||
const session: Session = await UserService.createUser(username, password, email, ip);
|
const session: Session = await UserService.createUser(username, password, email, ip);
|
||||||
|
|
||||||
// Send the session details back to the user
|
// Send the session details back to the user
|
||||||
res.cookie('betterauth', JSON.stringify({
|
res.status(201).send({
|
||||||
id: session.session_id,
|
session_id: session.session_id,
|
||||||
key: session.session_key
|
session_key: session.session_key
|
||||||
}), {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)}).status(201).send({});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Error handling a request: ' + e.message);
|
console.log('Error handling a request: ' + e.message);
|
||||||
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.'}));
|
||||||
@@ -80,10 +80,10 @@ usersRouter.post('/login', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the session details back to the user
|
// Send the session details back to the user
|
||||||
res.cookie('betterauth', JSON.stringify({
|
res.status(200).send({
|
||||||
id: session.session_id,
|
session_id: session.session_id,
|
||||||
key: session.session_key
|
session_key: session.session_key
|
||||||
}), {expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)}).status(200).send({});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Error handling a request: ' + e.message);
|
console.log('Error handling a request: ' + e.message);
|
||||||
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.'}));
|
||||||
@@ -94,9 +94,17 @@ usersRouter.post('/login', async (req: Request, res: Response) => {
|
|||||||
usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
|
usersRouter.post('/checkSessionValid', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const ip: string = req.connection.remoteAddress ?? '';
|
const ip: string = req.connection.remoteAddress ?? '';
|
||||||
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
|
||||||
|
if(!session_id || !session_key) {
|
||||||
|
// Error logging in, probably wrong username / password
|
||||||
|
res.status(401).send(JSON.stringify({messages: ['No session detected'], codes: [5]}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the user entry and create a session
|
// Update the user entry and create a session
|
||||||
const user: User = await UserService.checkSessionWithCookie(req.cookies.betterauth, ip);
|
const user: User = await UserService.checkSession(session_id, session_key, ip);
|
||||||
|
|
||||||
if (!user.user_id) {
|
if (!user.user_id) {
|
||||||
// Error logging in, probably wrong username / password
|
// Error logging in, probably wrong username / password
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ export const login = async (username: string, password: string, ip: string): Pro
|
|||||||
const sessionKeyHash = bcrypt.hashSync(sessionKey, 10);
|
const sessionKeyHash = bcrypt.hashSync(sessionKey, 10);
|
||||||
|
|
||||||
// Update user entry in SQL
|
// Update user entry in SQL
|
||||||
const userQuery = 'UPDATE users SET last_login_date = NOW()';
|
const userQuery = 'UPDATE users SET last_login_date = NOW() WHERE user_id = ?';
|
||||||
const userIdRes = await conn.query(userQuery);
|
const userIdRes = await conn.query(userQuery, userId);
|
||||||
await conn.commit();
|
await conn.commit();
|
||||||
|
|
||||||
// Create session
|
// Create session
|
||||||
|
|||||||
+12
-4
@@ -37,7 +37,9 @@ vendorsRouter.get('/managed', async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
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 vendors = await VendorService.getManagedShops(user.user_id);
|
const vendors = await VendorService.getManagedShops(user.user_id);
|
||||||
|
|
||||||
@@ -91,7 +93,9 @@ vendorsRouter.put('/manage/deactivatelisting', async (req: Request, res: Respons
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const vendor_id = req.body.vendor_id;
|
const vendor_id = req.body.vendor_id;
|
||||||
@@ -115,7 +119,9 @@ vendorsRouter.put('/manage/shop/deactivate/:id', async (req: Request, res: Respo
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const vendor_id = parseInt(req.params.id, 10);
|
const vendor_id = parseInt(req.params.id, 10);
|
||||||
@@ -138,7 +144,9 @@ vendorsRouter.put('/manage/shop/activate/:id', async (req: Request, res: Respons
|
|||||||
try {
|
try {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
const user_ip = req.connection.remoteAddress ?? '';
|
const user_ip = req.connection.remoteAddress ?? '';
|
||||||
const user = await UserService.checkSessionWithCookie(req.cookies.betterauth, user_ip);
|
const session_id = req.body.session_id;
|
||||||
|
const session_key = req.body.session_key;
|
||||||
|
const user = await UserService.checkSession(session_id, session_key, user_ip);
|
||||||
|
|
||||||
// Get required parameters
|
// Get required parameters
|
||||||
const vendor_id = parseInt(req.params.id, 10);
|
const vendor_id = parseInt(req.params.id, 10);
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import stepdefs.Preconditions;
|
|||||||
|
|
||||||
@RunWith(Cucumber.class)
|
@RunWith(Cucumber.class)
|
||||||
@CucumberOptions(
|
@CucumberOptions(
|
||||||
features = {"src/test/resource/searchProduct.feature",
|
features = {"src/test/resource/searchProduct.feature",
|
||||||
"src/test/resource/priceAlarms.feature"}
|
"src/test/resource/priceAlarms.feature",
|
||||||
|
"src/test/resource/favoriteShopList.feature",
|
||||||
|
"src/test/resource/manageVendor.feature"}
|
||||||
)
|
)
|
||||||
|
|
||||||
public class RunTest {
|
public class RunTest {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setup() {
|
public static void setup() {
|
||||||
Preconditions.driver= new FirefoxDriver();
|
Preconditions.driver = new FirefoxDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package stepdefs;
|
||||||
|
|
||||||
|
import io.cucumber.java.en.Given;
|
||||||
|
import io.cucumber.java.en.Then;
|
||||||
|
import io.cucumber.java.en.When;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
|
public class FavoriteShopList {
|
||||||
|
@Given("^the user has at least (\\d+) favorite shop$")
|
||||||
|
public void the_user_has_at_least_favorite_shop(int arg1) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^the profile page should open$")
|
||||||
|
public void the_profile_page_should_open() throws Exception {
|
||||||
|
WebElement profile_info_text = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover")));
|
||||||
|
assert(profile_info_text.isDisplayed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^he should see his favorite shops list$")
|
||||||
|
public void he_should_see_his_favorite_shops_list() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^he clicks on delete a favorite shop entry$")
|
||||||
|
public void he_clicks_on_delete_a_favorite_shop_entry() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^the favorite shop entry should be deleted$")
|
||||||
|
public void the_favorite_shop_entry_should_be_deleted() throws Exception {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package stepdefs;
|
||||||
|
|
||||||
|
import io.cucumber.java.en.Given;
|
||||||
|
import io.cucumber.java.en.Then;
|
||||||
|
import io.cucumber.java.en.When;
|
||||||
|
|
||||||
|
public class ManageVendor {
|
||||||
|
@Given("^the user is logged in as vendor manager$")
|
||||||
|
public void the_user_is_logged_in_as_vendor_manager() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the user opens the shop managing page$")
|
||||||
|
public void the_user_opens_the_shop_managing_page() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the user clicks on deactivate a listing$")
|
||||||
|
public void the_user_clicks_on_deactivate_a_listing() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^the listing should be deactivated$")
|
||||||
|
public void the_listing_should_be_deactivated() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the user clicks on deactivate the shop$")
|
||||||
|
public void the_user_clicks_on_deactivate_the_shop() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^the shop and all related listings should be deactivated$")
|
||||||
|
public void the_shop_and_all_related_listings_should_be_deactivated() throws Exception {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,4 +4,5 @@ import org.openqa.selenium.WebDriver;
|
|||||||
|
|
||||||
public class Preconditions {
|
public class Preconditions {
|
||||||
public static WebDriver driver;
|
public static WebDriver driver;
|
||||||
|
public static final int delaySeconds = 7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ import io.cucumber.java.PendingException;
|
|||||||
import io.cucumber.java.en.Given;
|
import io.cucumber.java.en.Given;
|
||||||
import io.cucumber.java.en.Then;
|
import io.cucumber.java.en.Then;
|
||||||
import io.cucumber.java.en.When;
|
import io.cucumber.java.en.When;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PriceAlarm {
|
public class PriceAlarm {
|
||||||
@Given("^the user has at least (\\d+) price alarm set$")
|
@Given("^the user has at least (\\d+) price alarm set$")
|
||||||
@@ -12,42 +18,43 @@ public class PriceAlarm {
|
|||||||
|
|
||||||
@When("^the user clicks on the profile icon$")
|
@When("^the user clicks on the profile icon$")
|
||||||
public void the_user_clicks_on_the_profile_icon() throws Exception {
|
public void the_user_clicks_on_the_profile_icon() throws Exception {
|
||||||
}
|
WebElement profileButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'profile')]")));
|
||||||
@Then("^the profile details popup should open$")
|
profileButton.click();
|
||||||
public void the_profile_details_popup_should_open() throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@When("^the user clicks on price alarms$")
|
|
||||||
public void the_user_clicks_on_price_alarms() throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Then("^the price alarm list should open$")
|
|
||||||
public void the_price_alarm_list_should_open() throws Exception {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the price alarm list should contain at least (\\d+) entry$")
|
@Then("^the price alarm list should contain at least (\\d+) entry$")
|
||||||
public void the_price_alarm_list_should_contain_at_least_entry(int arg1) throws Exception {
|
public void the_price_alarm_list_should_contain_at_least_entry(int arg1) throws Exception {
|
||||||
|
WebElement alarmEntry = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover tr:nth-child(2)")));
|
||||||
|
|
||||||
|
assert (alarmEntry != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the price alarm list should contain a maximum of (\\d+) entries per page$")
|
@Given("^the user is on the profile page$")
|
||||||
public void the_price_alarm_list_should_contain_a_maximum_of_entries_per_page(int arg1) throws Exception {
|
public void the_user_is_on_the_profile_page() throws Exception {
|
||||||
}
|
Preconditions.driver.get("https://www.betterzon.xyz/profile");
|
||||||
|
|
||||||
@Given("^the user is on the price alarm list page$")
|
WebElement profile_info_text = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
public void the_user_is_on_the_price_alarm_list_page() throws Exception {
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-user-information")));
|
||||||
|
assert (profile_info_text.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user clicks on the \"([^\"]*)\" button next to a price alarm$")
|
@When("^the user clicks on the \"([^\"]*)\" button next to a price alarm$")
|
||||||
public void the_user_clicks_on_the_button_next_to_a_price_alarm(String arg1) throws Exception {
|
public void the_user_clicks_on_the_button_next_to_a_price_alarm(String arg1) throws Exception {
|
||||||
}
|
if (arg1.equals("remove")) {
|
||||||
|
WebElement entry = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover tr:nth-child(2)")));
|
||||||
|
|
||||||
@Then("^a popup should open asking the user to confirm the removal$")
|
if (entry == null) {
|
||||||
public void a_popup_should_open_asking_the_user_to_confirm_the_removal() throws Exception {
|
throw new Exception("Too few price alarm entries found!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user confirms the removal of the price alarm$")
|
WebElement btn = entry.findElement(By.cssSelector("img.delete[src='../assets/images/Delete_icon-icons.com_55931.png']"));
|
||||||
public void the_user_confirms_the_removal_of_the_price_alarm() throws Exception {
|
|
||||||
|
btn.click();
|
||||||
|
} else if (arg1.equals("edit")) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the price alarm should be removed from the database$")
|
@Then("^the price alarm should be removed from the database$")
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import io.cucumber.java.PendingException;
|
|||||||
import io.cucumber.java.en.Given;
|
import io.cucumber.java.en.Given;
|
||||||
import io.cucumber.java.en.Then;
|
import io.cucumber.java.en.Then;
|
||||||
import io.cucumber.java.en.When;
|
import io.cucumber.java.en.When;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.Keys;
|
|
||||||
import org.openqa.selenium.WebElement;
|
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
@@ -15,8 +13,8 @@ public class SearchProduct {
|
|||||||
public void the_user_is_on_the_landing_page() throws Exception {
|
public void the_user_is_on_the_landing_page() throws Exception {
|
||||||
//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, Preconditions.delaySeconds))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".logo")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("a.navbar-brand")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
||||||
@@ -24,49 +22,99 @@ public class SearchProduct {
|
|||||||
WebElement searchField = Preconditions.driver.findElement(By.cssSelector(".ng-untouched.ng-pristine.ng-valid"));
|
WebElement searchField = Preconditions.driver.findElement(By.cssSelector(".ng-untouched.ng-pristine.ng-valid"));
|
||||||
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, Preconditions.delaySeconds))
|
||||||
.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 \"([^\"]*)\"$")
|
||||||
public void the_user_should_see_the_error_page(String arg0) throws Exception {
|
public void the_user_should_see_the_error_page(String arg0) throws Exception {
|
||||||
WebElement noProdsFoundMsg = (new WebDriverWait(Preconditions.driver, 10))
|
WebElement noProdsFoundMsg = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".ng-star-inserted")));
|
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'No Products found!')]")));
|
||||||
assert(noProdsFoundMsg.getText().contains("No Products found!"));
|
assert (noProdsFoundMsg.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Given("^the user is not logged in$")
|
@Given("^the user is not logged in$")
|
||||||
public void the_user_is_not_logged_in() throws Exception {
|
public void the_user_is_not_logged_in() throws Exception {
|
||||||
|
try {
|
||||||
|
WebElement logoutButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'log out')]")));
|
||||||
|
|
||||||
|
logoutButton.click();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Given("^the user is logged in$")
|
@Given("^the user is logged in$")
|
||||||
public void the_user_is_logged_in() throws Exception {
|
public void the_user_is_logged_in() throws Exception {
|
||||||
|
try {
|
||||||
|
WebElement loginButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'sign in')]")));
|
||||||
|
loginButton.click();
|
||||||
|
|
||||||
|
WebElement usernameField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.id("username")));
|
||||||
|
usernameField.sendKeys("Selenium");
|
||||||
|
|
||||||
|
WebElement passwordField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.id("password")));
|
||||||
|
passwordField.sendKeys("Selenium");
|
||||||
|
|
||||||
|
WebElement loginBtn = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.className("btn_signin")));
|
||||||
|
loginBtn.click();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see a list of products$")
|
@Then("^the user should see a list of products$")
|
||||||
public void the_user_should_see_a_list_of_products() throws Exception {
|
public void the_user_should_see_a_list_of_products() throws Exception {
|
||||||
WebElement product = (new WebDriverWait(Preconditions.driver, 10))
|
WebElement product = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".productItem.ng-star-inserted")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".row.p-2.bg-white.border.rounded")));
|
||||||
assert(product.isDisplayed());
|
assert (product.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user clicks on the first product$")
|
@When("^the user clicks on the first product$")
|
||||||
public void the_user_clicks_on_the_first_product() throws Exception {
|
public void the_user_clicks_on_the_first_product() throws Exception {
|
||||||
|
WebElement productDetailsBtn = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".row.p-2.bg-white.border.rounded button.btn.btn-primary.btn-sm")));
|
||||||
|
productDetailsBtn.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see the product detail page$")
|
@Then("^the user should see the product detail page$")
|
||||||
public void the_user_should_see_the_product_detail_page() throws Exception {
|
public void the_user_should_see_the_product_detail_page() throws Exception {
|
||||||
|
WebElement productTitle = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.productTitle")));
|
||||||
|
assert (productTitle.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the set price alarm box should show \"([^\"]*)\"$")
|
@Then("^the set price alarm box should show \"([^\"]*)\"$")
|
||||||
public void the_set_price_alarm_box_should_show(String arg0) throws Exception {
|
public void the_set_price_alarm_box_should_show(String arg0) throws Exception {
|
||||||
|
WebElement alarmBox = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm")));
|
||||||
|
if (arg0.equals("Login to set a price alarm")) {
|
||||||
|
assert (alarmBox.getText().equals("Login to set a price alarm"));
|
||||||
|
} else {
|
||||||
|
assert (alarmBox.isDisplayed());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user sets a price alarm$")
|
@When("^the user sets a price alarm$")
|
||||||
public void the_user_sets_a_price_alarm() throws Exception {
|
public void the_user_sets_a_price_alarm() throws Exception {
|
||||||
|
WebElement alarmBoxField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm input")));
|
||||||
|
alarmBoxField.sendKeys("12345");
|
||||||
|
WebElement alarmBox = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm")));
|
||||||
|
alarmBox.click();
|
||||||
|
|
||||||
|
assert (alarmBox.isDisplayed() && alarmBoxField.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should receive an email confirming the price alarm$")
|
@Then("^the user should receive an email confirming the price alarm$")
|
||||||
public void the_user_should_receive_an_email_confirming_the_price_alarm() throws Exception {
|
public void the_user_should_receive_an_email_confirming_the_price_alarm() throws Exception {
|
||||||
|
assert (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
Feature: Favorite Shop List
|
||||||
|
|
||||||
|
Scenario: Access Favorite Shop List
|
||||||
|
Given the user is on the landing page
|
||||||
|
And the user is logged in
|
||||||
|
And the user has at least 1 favorite shop
|
||||||
|
When the user clicks on the profile icon
|
||||||
|
Then the profile page should open
|
||||||
|
Then he should see his favorite shops list
|
||||||
|
|
||||||
|
Scenario: Remove Favorite Shop Entry
|
||||||
|
Given the user is on the landing page
|
||||||
|
And the user is logged in
|
||||||
|
And the user has at least 1 favorite shop
|
||||||
|
When the user clicks on the profile icon
|
||||||
|
Then the profile page should open
|
||||||
|
When he clicks on delete a favorite shop entry
|
||||||
|
Then the favorite shop entry should be deleted
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
Feature: Manage Vendor Shop
|
||||||
|
|
||||||
|
Scenario: Deactivate Product Listing
|
||||||
|
Given the user is on the landing page
|
||||||
|
And the user is logged in as vendor manager
|
||||||
|
When the user opens the shop managing page
|
||||||
|
And the user clicks on deactivate a listing
|
||||||
|
Then the listing should be deactivated
|
||||||
|
|
||||||
|
Scenario: Deactivate Shop Completely
|
||||||
|
Given the user is on the landing page
|
||||||
|
And the user is logged in as vendor manager
|
||||||
|
When the user opens the shop managing page
|
||||||
|
And the user clicks on deactivate the shop
|
||||||
|
Then the shop and all related listings should be deactivated
|
||||||
@@ -5,23 +5,22 @@ Feature: Price Alarms
|
|||||||
And the user is logged in
|
And the user is logged in
|
||||||
And the user has at least 1 price alarm set
|
And the user has at least 1 price alarm set
|
||||||
When the user clicks on the profile icon
|
When the user clicks on the profile icon
|
||||||
Then the profile details popup should open
|
Then the profile page should open
|
||||||
When the user clicks on price alarms
|
|
||||||
Then the price alarm list should open
|
|
||||||
And the price alarm list should contain at least 1 entry
|
And the price alarm list should contain at least 1 entry
|
||||||
And the price alarm list should contain a maximum of 20 entries per page
|
|
||||||
|
|
||||||
Scenario: Remove a price alarm
|
Scenario: Remove a price alarm
|
||||||
Given the user is on the price alarm list page
|
Given the user is on the landing page
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
|
When the user clicks on the profile icon
|
||||||
|
Then the profile page should open
|
||||||
When the user clicks on the "remove" button next to a price alarm
|
When the user clicks on the "remove" button next to a price alarm
|
||||||
Then a popup should open asking the user to confirm the removal
|
|
||||||
When the user confirms the removal of the price alarm
|
|
||||||
Then the price alarm should be removed from the database
|
Then the price alarm should be removed from the database
|
||||||
|
|
||||||
Scenario: Edit a price alarm
|
Scenario: Edit a price alarm
|
||||||
Given the user is on the price alarm list page
|
Given the user is on the landing page
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
|
When the user clicks on the profile icon
|
||||||
|
Then the profile page should open
|
||||||
When the user clicks on the "edit" button next to a price alarm
|
When the user clicks on the "edit" button next to a price alarm
|
||||||
Then a popup should open where the user can edit the alarm
|
Then a popup should open where the user can edit the alarm
|
||||||
When the user clicks on the "save changes" button
|
When the user clicks on the "save changes" button
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Feature: Search a Product
|
|||||||
Then the user should see a list of products
|
Then the user should see a list of products
|
||||||
When the user clicks on the first product
|
When the user clicks on the first product
|
||||||
Then the user should see the product detail page
|
Then the user should see the product detail page
|
||||||
And the set price alarm box should show "Log in to continue"
|
And the set price alarm box should show "Login to set a price alarm"
|
||||||
|
|
||||||
Scenario: User is logged in, searches for known product
|
Scenario: User is logged in, searches for known product
|
||||||
Given the user is on the landing page
|
Given the user is on the landing page
|
||||||
|
|||||||
+31
-28
@@ -2,32 +2,35 @@
|
|||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||||
|
|
||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
require('karma-firefox-launcher'),
|
require('karma-firefox-launcher'),
|
||||||
require('karma-chrome-launcher'),
|
require('karma-chrome-launcher'),
|
||||||
require('karma-jasmine-html-reporter'),
|
require('karma-jasmine-html-reporter'),
|
||||||
require('karma-coverage-istanbul-reporter'),
|
require('karma-coverage-istanbul-reporter'),
|
||||||
require('@angular-devkit/build-angular/plugins/karma')
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
],
|
],
|
||||||
client: {
|
client: {
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||||
},
|
jasmine: {
|
||||||
coverageIstanbulReporter: {
|
random: false
|
||||||
dir: require('path').join(__dirname, './coverage/Betterzon'),
|
}
|
||||||
reports: ['html', 'lcovonly', 'text-summary'],
|
},
|
||||||
fixWebpackSourcePaths: true
|
coverageIstanbulReporter: {
|
||||||
},
|
dir: require('path').join(__dirname, './coverage/Betterzon'),
|
||||||
reporters: ['progress', 'kjhtml'],
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
port: 9876,
|
fixWebpackSourcePaths: true
|
||||||
colors: true,
|
},
|
||||||
logLevel: config.LOG_INFO,
|
reporters: ['progress', 'kjhtml'],
|
||||||
autoWatch: true,
|
port: 9876,
|
||||||
browsers: ['Firefox'],
|
colors: true,
|
||||||
singleRun: false,
|
logLevel: config.LOG_INFO,
|
||||||
restartOnFileChange: true
|
autoWatch: true,
|
||||||
});
|
browsers: ['Firefox'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
.wrapper_app {
|
.wrapper_app {
|
||||||
padding-bottom: 2.5rem; /* Footer height */
|
padding-bottom: 2.5rem; /* Footer height */
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer_app {
|
.footer_app {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2.5rem; /* Footer height */
|
height: 2.5rem; /* Footer height */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
import {RouterTestingModule} from "@angular/router/testing";
|
import {RouterTestingModule} from '@angular/router/testing';
|
||||||
import {NgcCookieConsentConfig, NgcCookieConsentModule} from "ngx-cookieconsent";
|
import {NgcCookieConsentConfig, NgcCookieConsentModule} from 'ngx-cookieconsent';
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from '@angular/forms';
|
||||||
|
|
||||||
// For cookie consent module testing
|
// For cookie consent module testing
|
||||||
const cookieConfig: NgcCookieConsentConfig = {
|
const cookieConfig: NgcCookieConsentConfig = {
|
||||||
|
|||||||
@@ -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,17 @@ 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
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -23,21 +23,23 @@ import {NgcCookieConsentModule, NgcCookieConsentConfig} from 'ngx-cookieconsent'
|
|||||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||||
import {TopBarComponent} from './components/top-bar/top-bar.component';
|
import {TopBarComponent} from './components/top-bar/top-bar.component';
|
||||||
import {RouterModule} from '@angular/router';
|
import {RouterModule} from '@angular/router';
|
||||||
import {MatButtonModule} from "@angular/material/button";
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||||
import {MatIconModule} from '@angular/material/icon';
|
import {MatIconModule} from '@angular/material/icon';
|
||||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||||
import {MatListModule} from "@angular/material/list";
|
import {MatListModule} from '@angular/material/list';
|
||||||
import {BottomBarComponent} from './components/bottom-bar/bottom-bar.component';
|
import {BottomBarComponent} from './components/bottom-bar/bottom-bar.component';
|
||||||
import { HotDealsWidgetComponent } from './components/hot-deals-widget/hot-deals-widget.component';
|
import {HotDealsWidgetComponent} from './components/hot-deals-widget/hot-deals-widget.component';
|
||||||
import { SliderForProductsComponent } from './components/slider-for-products/slider-for-products.component';
|
import {SliderForProductsComponent} from './components/slider-for-products/slider-for-products.component';
|
||||||
import { RegistrationComponent } from './components/auth/registration/registration.component';
|
import {RegistrationComponent} from './components/auth/registration/registration.component';
|
||||||
import { MatCardModule } from "@angular/material/card";
|
import {MatCardModule} from '@angular/material/card';
|
||||||
import {SigninComponent} from "./components/auth/signin/signin.component";
|
import {SigninComponent} from './components/auth/signin/signin.component';
|
||||||
import { CopyrightComponent } from './components/copyright/copyright.component';
|
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 './pages/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,
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import {ProductSearchPageComponent} from './pages/product-search-page/product-se
|
|||||||
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 {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 './pages/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'},
|
||||||
@@ -20,7 +22,8 @@ const routes: Routes = [
|
|||||||
{path: 'datenschutz', component: PrivacyComponent},
|
{path: 'datenschutz', component: PrivacyComponent},
|
||||||
{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,10 @@
|
|||||||
</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
|
||||||
<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>
|
alternatives to the de-facto monopolist Amazon?</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>
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { AboutUsComponent } from './about-us.component';
|
import {AboutUsComponent} from './about-us.component';
|
||||||
|
|
||||||
describe('AboutUsComponent', () => {
|
describe('AboutUsComponent', () => {
|
||||||
let component: AboutUsComponent;
|
let component: AboutUsComponent;
|
||||||
let fixture: ComponentFixture<AboutUsComponent>;
|
let fixture: ComponentFixture<AboutUsComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ AboutUsComponent ]
|
declarations: [AboutUsComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AboutUsComponent);
|
fixture = TestBed.createComponent(AboutUsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-about-us',
|
selector: 'app-about-us',
|
||||||
templateUrl: './about-us.component.html',
|
templateUrl: './about-us.component.html',
|
||||||
styleUrls: ['./about-us.component.css']
|
styleUrls: ['./about-us.component.css']
|
||||||
})
|
})
|
||||||
export class AboutUsComponent implements OnInit {
|
export class AboutUsComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import {Routes, RouterModule} from '@angular/router';
|
||||||
import {RegistrationComponent} from "./registration/registration.component";
|
import {RegistrationComponent} from './registration/registration.component';
|
||||||
import {SigninComponent} from "./signin/signin.component";
|
import {SigninComponent} from './signin/signin.component';
|
||||||
import {ResetpasswortComponent} from "./resetpasswort/resetpasswort.component";
|
import {ResetpasswortComponent} from './resetpasswort/resetpasswort.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -20,7 +20,8 @@ const routes: Routes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule]
|
exports: [RouterModule]
|
||||||
})
|
})
|
||||||
export class AuthRoutingModule { }
|
export class AuthRoutingModule {
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import {NgModule} from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
import { AuthRoutingModule } from './auth-routing.module';
|
import {AuthRoutingModule} from './auth-routing.module';
|
||||||
import { SigninComponent } from "./signin/signin.component";
|
import {SigninComponent} from './signin/signin.component';
|
||||||
import { RegistrationComponent } from './registration/registration.component';
|
import {RegistrationComponent} from './registration/registration.component';
|
||||||
import { ResetpasswortComponent } from './resetpasswort/resetpasswort.component';
|
import {ResetpasswortComponent} from './resetpasswort/resetpasswort.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [SigninComponent, RegistrationComponent, ResetpasswortComponent],
|
declarations: [SigninComponent, RegistrationComponent, ResetpasswortComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
AuthRoutingModule,
|
AuthRoutingModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SigninComponent,
|
SigninComponent,
|
||||||
RegistrationComponent,
|
RegistrationComponent,
|
||||||
ResetpasswortComponent,
|
ResetpasswortComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AuthModule { }
|
export class AuthModule {
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
.main-content{
|
.main-content {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
box-shadow: 0 5px 5px rgba(0,0,0,.4);
|
box-shadow: 0 5px 5px rgba(0, 0, 0, .4);
|
||||||
margin: 5em auto;
|
margin: 5em auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.company__info{
|
|
||||||
|
.company__info {
|
||||||
background-color: #008080;
|
background-color: #008080;
|
||||||
border-top-left-radius: 20px;
|
border-top-left-radius: 20px;
|
||||||
border-bottom-left-radius: 20px;
|
border-bottom-left-radius: 20px;
|
||||||
@@ -14,63 +15,79 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.fa-android{
|
|
||||||
font-size:3em;
|
.fa-android {
|
||||||
|
font-size: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
@media screen and (max-width: 640px) {
|
||||||
.main-content{width: 90%;}
|
.main-content {
|
||||||
.company__info{
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company__info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.login_form{
|
|
||||||
border-top-left-radius:20px;
|
.login_form {
|
||||||
border-bottom-left-radius:20px;
|
border-top-left-radius: 20px;
|
||||||
|
border-bottom-left-radius: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 642px) and (max-width:800px){
|
|
||||||
.main-content{width: 70%;}
|
@media screen and (min-width: 642px) and (max-width: 800px) {
|
||||||
|
.main-content {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.row > h2{
|
|
||||||
color:#008080;
|
.row > h2 {
|
||||||
|
color: #008080;
|
||||||
}
|
}
|
||||||
.login_form{
|
|
||||||
|
.login_form {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top-right-radius:20px;
|
border-top-right-radius: 20px;
|
||||||
border-bottom-right-radius:20px;
|
border-bottom-right-radius: 20px;
|
||||||
border-top:1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
border-right:1px solid #ccc;
|
border-right: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
form{
|
|
||||||
|
form {
|
||||||
padding: 0 2em;
|
padding: 0 2em;
|
||||||
}
|
}
|
||||||
.form__input{
|
|
||||||
|
.form__input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border:0px solid transparent;
|
border: 0px solid transparent;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-bottom: 1px solid #aaa;
|
border-bottom: 1px solid #aaa;
|
||||||
padding: 1em .5em .5em;
|
padding: 1em .5em .5em;
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
outline:none;
|
outline: none;
|
||||||
margin:1.5em auto;
|
margin: 1.5em auto;
|
||||||
transition: all .5s ease;
|
transition: all .5s ease;
|
||||||
}
|
}
|
||||||
.form__input:focus{
|
|
||||||
|
.form__input:focus {
|
||||||
border-bottom-color: #008080;
|
border-bottom-color: #008080;
|
||||||
box-shadow: 0 0 5px rgba(0,80,80,.4);
|
box-shadow: 0 0 5px rgba(0, 80, 80, .4);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.btn_signin{
|
|
||||||
|
.btn_signin {
|
||||||
transition: all .5s ease;
|
transition: all .5s ease;
|
||||||
width: 70%;
|
width: 100%;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
color:#008080;
|
color: #008080;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #008080;
|
border: 1px solid #008080;
|
||||||
margin-top: 1.5em;
|
margin-top: 1.5em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
.btn_signin:hover, .btn:focus{
|
|
||||||
|
.btn_signin:hover, .btn:focus {
|
||||||
background-color: #008080;
|
background-color: #008080;
|
||||||
color:#fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,36 @@
|
|||||||
<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 control="" class="form-group">
|
<form [formGroup]="form" class="form-group" (ngSubmit)="onSubmit()">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="text" name="username" id="username" class="form__input" placeholder="Nickname">
|
<input type="text" formControlName="username" id="username" name="username"
|
||||||
|
class="form__input" placeholder="Username">
|
||||||
|
<div *ngIf="submitted && me.username.errors" class="invalid-feedback">
|
||||||
|
<div *ngIf="me.username.errors.required">Username is required</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- <span class="fa fa-lock"></span> -->
|
<!-- <span class="fa fa-lock"></span> -->
|
||||||
<input type="password" name="password" id="email" class="form__input" placeholder= "E-Mail">
|
<input type="email" formControlName="email" name="email" id="email" class="form__input"
|
||||||
|
placeholder="E-Mail">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- <span class="fa fa-lock"></span> -->
|
<!-- <span class="fa fa-lock"></span> -->
|
||||||
<input type="password" name="password" id="password" class="form__input" placeholder="Kennwort erstellen">
|
<input type="password" formControlName="password" name="password" id="password"
|
||||||
|
class="form__input" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- <span class="fa fa-lock"></span> -->
|
|
||||||
<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 class="row">
|
||||||
|
<input type="submit" value="Sign up" class="btn_signin">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="submit" value="Erstellen" class="btn_signin">
|
<p>Have an account?<a href="/signin">Log In</a></p>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<p>Haben Sie bereits ein Konto?<a href="/signin">Sich anmelden</a></p>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,54 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { RegistrationComponent } from './registration.component';
|
import {RegistrationComponent} from './registration.component';
|
||||||
|
import {AbstractMockObservableService} from '../../../mocks/mock.service';
|
||||||
|
import {ApiService} from '../../../services/api.service';
|
||||||
|
import {FormBuilder, FormControl, Validators} from '@angular/forms';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
registerUser(username: string, password: string, email: string): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('RegistrationComponent', () => {
|
describe('RegistrationComponent', () => {
|
||||||
let component: RegistrationComponent;
|
let component: RegistrationComponent;
|
||||||
let fixture: ComponentFixture<RegistrationComponent>;
|
let fixture: ComponentFixture<RegistrationComponent>;
|
||||||
|
let mockService;
|
||||||
|
let formBuilder: FormBuilder;
|
||||||
|
const router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
routerState: jasmine.createSpy('routerState')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
mockService = new MockApiService();
|
||||||
declarations: [ RegistrationComponent ]
|
await TestBed.configureTestingModule({
|
||||||
})
|
declarations: [RegistrationComponent],
|
||||||
.compileComponents();
|
providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}, FormBuilder]
|
||||||
});
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(RegistrationComponent);
|
fixture = TestBed.createComponent(RegistrationComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
formBuilder = TestBed.get(FormBuilder);
|
||||||
});
|
component.form = formBuilder.group({
|
||||||
|
recipientTypes: new FormControl(
|
||||||
|
{
|
||||||
|
value: ['mock'],
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
Validators.required
|
||||||
|
)
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,47 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||||
|
import {ApiService} from '../../../services/api.service';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-registration',
|
selector: 'app-registration',
|
||||||
templateUrl: './registration.component.html',
|
templateUrl: './registration.component.html',
|
||||||
styleUrls: ['./registration.component.css']
|
styleUrls: ['./registration.component.css']
|
||||||
})
|
})
|
||||||
export class RegistrationComponent implements OnInit {
|
export class RegistrationComponent implements OnInit {
|
||||||
|
form: any;
|
||||||
|
loading = false;
|
||||||
|
submitted = false;
|
||||||
|
|
||||||
constructor() { }
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private api: ApiService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
this.form = this.formBuilder.group({
|
||||||
|
username: ['', Validators.required],
|
||||||
|
email: ['', Validators.required],
|
||||||
|
password: ['', [
|
||||||
|
Validators.required,
|
||||||
|
Validators.minLength(8)]
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get me() {
|
||||||
|
return this.form.controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(): void {
|
||||||
|
this.api.registerUser(this.form.value.username, this.form.value.password, this.form.value.email).subscribe(
|
||||||
|
res => {
|
||||||
|
this.api.saveSessionInfoToLocalStorage(res);
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { ResetpasswortComponent } from './resetpasswort.component';
|
import {ResetpasswortComponent} from './resetpasswort.component';
|
||||||
|
|
||||||
describe('ResetpasswortComponent', () => {
|
describe('ResetpasswortComponent', () => {
|
||||||
let component: ResetpasswortComponent;
|
let component: ResetpasswortComponent;
|
||||||
let fixture: ComponentFixture<ResetpasswortComponent>;
|
let fixture: ComponentFixture<ResetpasswortComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ ResetpasswortComponent ]
|
declarations: [ResetpasswortComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ResetpasswortComponent);
|
fixture = TestBed.createComponent(ResetpasswortComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-resetpasswort',
|
selector: 'app-resetpasswort',
|
||||||
templateUrl: './resetpasswort.component.html',
|
templateUrl: './resetpasswort.component.html',
|
||||||
styleUrls: ['./resetpasswort.component.css']
|
styleUrls: ['./resetpasswort.component.css']
|
||||||
})
|
})
|
||||||
export class ResetpasswortComponent implements OnInit {
|
export class ResetpasswortComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
.main-content{
|
.main-content {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
box-shadow: 0 5px 5px rgba(0,0,0,.4);
|
box-shadow: 0 5px 5px rgba(0, 0, 0, .4);
|
||||||
margin: 5em auto;
|
margin: 5em auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.company__info{
|
|
||||||
|
.company__info {
|
||||||
background-color: #008080;
|
background-color: #008080;
|
||||||
border-top-left-radius: 20px;
|
border-top-left-radius: 20px;
|
||||||
border-bottom-left-radius: 20px;
|
border-bottom-left-radius: 20px;
|
||||||
@@ -14,63 +15,79 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.fa-android{
|
|
||||||
font-size:3em;
|
.fa-android {
|
||||||
|
font-size: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
@media screen and (max-width: 640px) {
|
||||||
.main-content{width: 90%;}
|
.main-content {
|
||||||
.company__info{
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company__info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.login_form{
|
|
||||||
border-top-left-radius:20px;
|
.login_form {
|
||||||
border-bottom-left-radius:20px;
|
border-top-left-radius: 20px;
|
||||||
|
border-bottom-left-radius: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 642px) and (max-width:800px){
|
|
||||||
.main-content{width: 70%;}
|
@media screen and (min-width: 642px) and (max-width: 800px) {
|
||||||
|
.main-content {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.row > h2{
|
|
||||||
color:#008080;
|
.row > h2 {
|
||||||
|
color: #008080;
|
||||||
}
|
}
|
||||||
.login_form{
|
|
||||||
|
.login_form {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top-right-radius:20px;
|
border-top-right-radius: 20px;
|
||||||
border-bottom-right-radius:20px;
|
border-bottom-right-radius: 20px;
|
||||||
border-top:1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
border-right:1px solid #ccc;
|
border-right: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
form{
|
|
||||||
|
form {
|
||||||
padding: 0 2em;
|
padding: 0 2em;
|
||||||
}
|
}
|
||||||
.form__input{
|
|
||||||
|
.form__input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border:0px solid transparent;
|
border: 0px solid transparent;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-bottom: 1px solid #aaa;
|
border-bottom: 1px solid #aaa;
|
||||||
padding: 1em .5em .5em;
|
padding: 1em .5em .5em;
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
outline:none;
|
outline: none;
|
||||||
margin:1.5em auto;
|
margin: 1.5em auto;
|
||||||
transition: all .5s ease;
|
transition: all .5s ease;
|
||||||
}
|
}
|
||||||
.form__input:focus{
|
|
||||||
|
.form__input:focus {
|
||||||
border-bottom-color: #008080;
|
border-bottom-color: #008080;
|
||||||
box-shadow: 0 0 5px rgba(0,80,80,.4);
|
box-shadow: 0 0 5px rgba(0, 80, 80, .4);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.btn_signin{
|
|
||||||
|
.btn_signin {
|
||||||
transition: all .5s ease;
|
transition: all .5s ease;
|
||||||
width: 70%;
|
width: 100%;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
color:#008080;
|
color: #008080;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #008080;
|
border: 1px solid #008080;
|
||||||
margin-top: 1.5em;
|
margin-top: 1.5em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
.btn_signin:hover, .btn:focus{
|
|
||||||
|
.btn_signin:hover, .btn:focus {
|
||||||
background-color: #008080;
|
background-color: #008080;
|
||||||
color:#fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,24 +6,26 @@
|
|||||||
<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 control="" class="form-group">
|
<form [formGroup]="loginForm" class="form-group" (ngSubmit)="onSubmit()">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="text" name="username" id="username" class="form__input" placeholder="Username">
|
<input type="text" formControlName="username" name="username" id="username"
|
||||||
|
class="form__input" placeholder="Username">
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- <span class="fa fa-lock"></span> -->
|
<!-- <span class="fa fa-lock"></span> -->
|
||||||
<input type="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">
|
||||||
<p>Noch kein Konto?<a href="/registration">Konto erstellen</a></p>
|
<p>No account yet?<a href="/registration">sign up</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,54 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { SigninComponent } from './signin.component';
|
import {SigninComponent} from './signin.component';
|
||||||
|
import {AbstractMockObservableService} from '../../../mocks/mock.service';
|
||||||
|
import {ApiService} from '../../../services/api.service';
|
||||||
|
import {FormBuilder, FormControl, Validators} from '@angular/forms';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
loginUser(username: string, password: string): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('SigninComponent', () => {
|
describe('SigninComponent', () => {
|
||||||
let component: SigninComponent;
|
let component: SigninComponent;
|
||||||
let fixture: ComponentFixture<SigninComponent>;
|
let fixture: ComponentFixture<SigninComponent>;
|
||||||
|
let mockService;
|
||||||
|
let formBuilder: FormBuilder;
|
||||||
|
const router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
routerState: jasmine.createSpy('routerState')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
mockService = new MockApiService();
|
||||||
declarations: [ SigninComponent ]
|
await TestBed.configureTestingModule({
|
||||||
})
|
declarations: [SigninComponent],
|
||||||
.compileComponents();
|
providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}, FormBuilder]
|
||||||
});
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(SigninComponent);
|
fixture = TestBed.createComponent(SigninComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
formBuilder = TestBed.get(FormBuilder);
|
||||||
});
|
component.loginForm = formBuilder.group({
|
||||||
|
recipientTypes: new FormControl(
|
||||||
|
{
|
||||||
|
value: ['mock'],
|
||||||
|
disabled: true
|
||||||
|
},
|
||||||
|
Validators.required
|
||||||
|
)
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||||
|
import {ApiService} from '../../../services/api.service';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-signin',
|
selector: 'app-signin',
|
||||||
@@ -8,12 +11,44 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
|
|
||||||
export class SigninComponent implements OnInit {
|
export class SigninComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
loginForm: FormGroup;
|
||||||
|
loading = false;
|
||||||
|
submitted = false;
|
||||||
|
private isSuccessful: boolean;
|
||||||
|
private isSignUpFailed: boolean;
|
||||||
|
private errorMessage: '';
|
||||||
|
|
||||||
ngOnInit(): void {
|
constructor(
|
||||||
}
|
private formBuilder: FormBuilder,
|
||||||
|
private api: ApiService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
onSubmit() {
|
ngOnInit(): void {
|
||||||
|
this.loginForm = this.formBuilder.group({
|
||||||
|
username: ['', Validators.required],
|
||||||
|
password: ['', [Validators.required, Validators.minLength(8)]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(): void {
|
||||||
|
this.submitted = true;
|
||||||
|
|
||||||
|
if (this.loginForm.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.api.loginUser(this.loginForm.value.username, this.loginForm.value.password)
|
||||||
|
.subscribe(
|
||||||
|
data => {
|
||||||
|
this.isSuccessful = true;
|
||||||
|
this.router.navigate(['']);
|
||||||
|
this.api.saveSessionInfoToLocalStorage(data);
|
||||||
|
},
|
||||||
|
err => {
|
||||||
|
this.errorMessage = err.error.message;
|
||||||
|
this.isSignUpFailed = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.folge-uns-item {
|
.folge-uns-item {
|
||||||
grid-column: 2; grid-row: 1;
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-items {
|
.link-items {
|
||||||
grid-column: 2; grid-row: 2;
|
grid-column: 2;
|
||||||
|
grid-row: 2;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,11 +31,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bottom-logo {
|
.bottom-logo {
|
||||||
grid-column: 1; grid-row: 3;
|
grid-column: 1;
|
||||||
|
grid-row: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-info {
|
.bottom-info {
|
||||||
grid-column: 3; grid-row: 3;
|
grid-column: 3;
|
||||||
|
grid-row: 3;
|
||||||
justify-self: right;
|
justify-self: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,24 @@
|
|||||||
<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
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-twitter"></i></a>
|
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-linkedin-in"></i></a>
|
<a class="btn btn-outline-light btn-social mx-1" href="https://blog.betterzon.xyz/"><i
|
||||||
<a class="btn btn-outline-light btn-social mx-1" href="#!"><i class="fab fa-fw fa-dribbble"></i></a>
|
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,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { BottomBarComponent } from "./bottom-bar.component";
|
import {BottomBarComponent} from './bottom-bar.component';
|
||||||
|
|
||||||
describe("BottomBarComponent", () => {
|
describe('BottomBarComponent', () => {
|
||||||
let component: BottomBarComponent;
|
let component: BottomBarComponent;
|
||||||
let fixture: ComponentFixture<BottomBarComponent>;
|
let fixture: ComponentFixture<BottomBarComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ BottomBarComponent ]
|
declarations: [BottomBarComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(BottomBarComponent);
|
fixture = TestBed.createComponent(BottomBarComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-bottom-bar',
|
selector: 'app-bottom-bar',
|
||||||
templateUrl: "./bottom-bar.component.html",
|
templateUrl: './bottom-bar.component.html',
|
||||||
styleUrls: ["./bottom-bar.component.css"]
|
styleUrls: ['./bottom-bar.component.css']
|
||||||
})
|
})
|
||||||
export class BottomBarComponent implements OnInit {
|
export class BottomBarComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#imprintSection {
|
||||||
|
right: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 1em;
|
||||||
|
grid-area: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#imprintSection a {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#copyright {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
'left center right';
|
||||||
|
grid-template-columns: 30% 40% 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#copyright-text {
|
||||||
|
grid-area: center;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
<div class="copyright py-4 text-center text-white">
|
<div class="copyright py-4 text-center text-white" id="copyright">
|
||||||
<div class="container"><small>Copyright © Your Website 2021</small></div>
|
<div class="container" id="copyright-text"><small>Copyright © Betterzon 2021</small></div>
|
||||||
|
<div id="imprintSection">
|
||||||
|
<a href="/impressum">Imprint</a><br>
|
||||||
|
<a href="/datenschutz">Privacy Policy</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { CopyrightComponent } from './copyright.component';
|
import {CopyrightComponent} from './copyright.component';
|
||||||
|
|
||||||
describe('CopyrightComponent', () => {
|
describe('CopyrightComponent', () => {
|
||||||
let component: CopyrightComponent;
|
let component: CopyrightComponent;
|
||||||
let fixture: ComponentFixture<CopyrightComponent>;
|
let fixture: ComponentFixture<CopyrightComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ CopyrightComponent ]
|
declarations: [CopyrightComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(CopyrightComponent);
|
fixture = TestBed.createComponent(CopyrightComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-copyright',
|
selector: 'app-copyright',
|
||||||
templateUrl: './copyright.component.html',
|
templateUrl: './copyright.component.html',
|
||||||
styleUrls: ['./copyright.component.css']
|
styleUrls: ['./copyright.component.css']
|
||||||
})
|
})
|
||||||
export class CopyrightComponent implements OnInit {
|
export class CopyrightComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
<a href="https://blog.betterzon.xyz/" class="fa fa-info fa-4x icon-3d"></a>
|
<a href="https://blog.betterzon.xyz/" class="fa fa-info fa-4x icon-3d"></a>
|
||||||
<a href="https://github.com/Mueller-Patrick/Betterzon/wiki" class="fa fa-wikipedia-w 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>
|
||||||
<div id="imprintSection">
|
<div id="imprintSection">
|
||||||
<a href="/impressum" >Imprint</a><br>
|
<a href="/impressum">Imprint</a><br>
|
||||||
<a href="/datenschutz">Privacy Policy</a>
|
<a href="/datenschutz">Privacy Policy</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-footer',
|
selector: 'app-footer',
|
||||||
templateUrl: './footer.component.html',
|
templateUrl: './footer.component.html',
|
||||||
styleUrls: ['./footer.component.css']
|
styleUrls: ['./footer.component.css']
|
||||||
})
|
})
|
||||||
export class FooterComponent implements OnInit {
|
export class FooterComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateImprint(): void {
|
navigateImprint(): void {
|
||||||
this.router.navigate([('/impressum')]);
|
this.router.navigate([('/impressum')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<header class="masthead bg-primary text-white text-center">
|
<header class="masthead bg-primary text-white text-center">
|
||||||
<div class="container d-flex align-items-center flex-column">
|
<div class="container d-flex align-items-center flex-column">
|
||||||
<!-- Masthead Avatar Image-->
|
<!-- Masthead Avatar Image-->
|
||||||
<img class="masthead-avatar mb-5" src="assets/images/Betterzon.svg" alt="..." />
|
<img class="masthead-avatar mb-5" src="assets/images/Betterzon.svg" alt="..."/>
|
||||||
<!-- Masthead Heading-->
|
<!-- Masthead Heading-->
|
||||||
<h1 class="masthead-heading text-uppercase mb-0"></h1>
|
<h1 class="masthead-heading text-uppercase mb-0"></h1>
|
||||||
<!-- Icon Divider-->
|
<!-- Icon Divider-->
|
||||||
|
|||||||
+18
-18
@@ -1,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { GreetingInfoSliderComponent } from './greeting-info-slider.component';
|
import {GreetingInfoSliderComponent} from './greeting-info-slider.component';
|
||||||
|
|
||||||
describe('GreetingInfoSliderComponent', () => {
|
describe('GreetingInfoSliderComponent', () => {
|
||||||
let component: GreetingInfoSliderComponent;
|
let component: GreetingInfoSliderComponent;
|
||||||
let fixture: ComponentFixture<GreetingInfoSliderComponent>;
|
let fixture: ComponentFixture<GreetingInfoSliderComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ GreetingInfoSliderComponent ]
|
declarations: [GreetingInfoSliderComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(GreetingInfoSliderComponent);
|
fixture = TestBed.createComponent(GreetingInfoSliderComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-greeting-info-slider',
|
selector: 'app-greeting-info-slider',
|
||||||
templateUrl: './greeting-info-slider.component.html',
|
templateUrl: './greeting-info-slider.component.html',
|
||||||
styleUrls: ['./greeting-info-slider.component.css']
|
styleUrls: ['./greeting-info-slider.component.css']
|
||||||
})
|
})
|
||||||
export class GreetingInfoSliderComponent implements OnInit {
|
export class GreetingInfoSliderComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<img src="assets/images/Betterzon.svg" alt="Betterzon Logo" width="50px" (click)="clickedLogo()">
|
<img src="assets/images/Betterzon.svg" alt="Betterzon Logo" width="50px" (click)="clickedLogo()">
|
||||||
</div>
|
</div>
|
||||||
<div class="searchBox">
|
<div class="searchBox">
|
||||||
<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="slider">
|
<div class="slider">
|
||||||
<mat-slide-toggle color="primary">dark me</mat-slide-toggle>
|
<mat-slide-toggle color="primary">dark me</mat-slide-toggle>
|
||||||
|
|||||||
@@ -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-OFFERS</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,26 +9,33 @@
|
|||||||
<!-- 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">
|
<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>699.00$</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,58 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { HotDealsWidgetComponent } from './hot-deals-widget.component';
|
import {HotDealsWidgetComponent} from './hot-deals-widget.component';
|
||||||
|
import {AbstractMockObservableService} from '../../mocks/mock.service';
|
||||||
|
import {ApiService} from '../../services/api.service';
|
||||||
|
import {ActivatedRoute, convertToParamMap, Router} from '@angular/router';
|
||||||
|
import {Observable, of} from 'rxjs';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
getBestDeals(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProductsByIds(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivatedRouteMock {
|
||||||
|
public paramMap = of(convertToParamMap({
|
||||||
|
testId: 'abc123',
|
||||||
|
anotherId: 'd31e8b48-7309-4c83-9884-4142efdf7271',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
describe('HotDealsWidgetComponent', () => {
|
describe('HotDealsWidgetComponent', () => {
|
||||||
let component: HotDealsWidgetComponent;
|
let component: HotDealsWidgetComponent;
|
||||||
let fixture: ComponentFixture<HotDealsWidgetComponent>;
|
let fixture: ComponentFixture<HotDealsWidgetComponent>;
|
||||||
|
let mockService;
|
||||||
|
const router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
routerState: jasmine.createSpy('routerState')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
mockService = new MockApiService();
|
||||||
declarations: [ HotDealsWidgetComponent ]
|
await TestBed.configureTestingModule({
|
||||||
})
|
declarations: [HotDealsWidgetComponent],
|
||||||
.compileComponents();
|
providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}, {
|
||||||
});
|
provide: ActivatedRoute,
|
||||||
|
useValue: ActivatedRouteMock
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(HotDealsWidgetComponent);
|
fixture = TestBed.createComponent(HotDealsWidgetComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import {Product} from '../../models/product';
|
|||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hot-deals-widget',
|
selector: 'app-hot-deals-widget',
|
||||||
templateUrl: './hot-deals-widget.component.html',
|
templateUrl: './hot-deals-widget.component.html',
|
||||||
styleUrls: ['./hot-deals-widget.component.css']
|
styleUrls: ['./hot-deals-widget.component.css']
|
||||||
})
|
})
|
||||||
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>
|
||||||
@@ -12,25 +12,28 @@
|
|||||||
<div class="col-md-6 col-lg-4 mb-5">
|
<div class="col-md-6 col-lg-4 mb-5">
|
||||||
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
||||||
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
||||||
<div class="portfolio-item-caption-content text-center text-white"><i class="fas fa-plus fa-3x"></i></div>
|
<div class="portfolio-item-caption-content text-center text-white"><i
|
||||||
|
class="fas fa-plus fa-3x"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<img class="productImage" src="assets/images/Betterzon.svg"/>
|
<img width="100%" class="productImage" src="assets/images/cropped-unknown-1-1.png"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-4 mb-5">
|
<div class="col-md-6 col-lg-4 mb-5">
|
||||||
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
||||||
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
||||||
<div class="portfolio-item-caption-content text-center text-white"><i class="fas fa-plus fa-3x"></i></div>
|
<div class="portfolio-item-caption-content text-center text-white"><i
|
||||||
|
class="fas fa-plus fa-3x"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<img class="productImage" src="assets/images/Betterzon.svg"/>
|
<img width="100%" class="productImage" src="assets/images/plantshub.jpg"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-lg-4 mb-5">
|
<div class="col-md-6 col-lg-4 mb-5">
|
||||||
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
<div class="portfolio-item mx-auto" data-bs-toggle="modal" data-bs-target="#portfolioModal1">
|
||||||
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
<div class="portfolio-item-caption d-flex align-items-center justify-content-center h-100 w-100">
|
||||||
<div class="portfolio-item-caption-content text-center text-white"><i class="fas fa-plus fa-3x"></i></div>
|
<div class="portfolio-item-caption-content text-center text-white"><i
|
||||||
|
class="fas fa-plus fa-3x"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<img class="productImage" src="assets/images/Betterzon.svg"/>
|
<img width="70%" class="productImage" src="assets/images/CeangalLogo.png"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,53 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { KundenComponent } from './kunden.component';
|
import {KundenComponent} from './kunden.component';
|
||||||
|
import {AbstractMockObservableService} from '../../mocks/mock.service';
|
||||||
|
import {ApiService} from '../../services/api.service';
|
||||||
|
import {ActivatedRoute, convertToParamMap, Router} from '@angular/router';
|
||||||
|
import {of} from 'rxjs';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
getProducts(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActivatedRouteMock {
|
||||||
|
public paramMap = of(convertToParamMap({
|
||||||
|
testId: 'abc123',
|
||||||
|
anotherId: 'd31e8b48-7309-4c83-9884-4142efdf7271',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
describe('KundenComponent', () => {
|
describe('KundenComponent', () => {
|
||||||
let component: KundenComponent;
|
let component: KundenComponent;
|
||||||
let fixture: ComponentFixture<KundenComponent>;
|
let fixture: ComponentFixture<KundenComponent>;
|
||||||
|
let mockService;
|
||||||
|
const router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
routerState: jasmine.createSpy('routerState')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
mockService = new MockApiService();
|
||||||
declarations: [ KundenComponent ]
|
await TestBed.configureTestingModule({
|
||||||
})
|
declarations: [KundenComponent],
|
||||||
.compileComponents();
|
providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}, {
|
||||||
});
|
provide: ActivatedRoute,
|
||||||
|
useValue: ActivatedRouteMock
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(KundenComponent);
|
fixture = TestBed.createComponent(KundenComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import {Product} from '../../models/product';
|
|||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-kunden',
|
selector: 'app-kunden',
|
||||||
templateUrl: './kunden.component.html',
|
templateUrl: './kunden.component.html',
|
||||||
styleUrls: ['./kunden.component.css']
|
styleUrls: ['./kunden.component.css']
|
||||||
})
|
})
|
||||||
export class KundenComponent implements OnInit {
|
export class KundenComponent implements OnInit {
|
||||||
products: Product[] = [];
|
products: Product[] = [];
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ class MockApiService extends AbstractMockObservableService {
|
|||||||
this.content = [vendor];
|
this.content = [vendor];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSessionInfoFromLocalStorage(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ProductDetailsComponent', () => {
|
describe('ProductDetailsComponent', () => {
|
||||||
|
|||||||
@@ -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(): void {
|
||||||
|
this.apiService.createPriceAlarms(this.productId, this.price * 100).subscribe(
|
||||||
|
alarms => console.log(alarms)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,53 @@
|
|||||||
<div *ngIf="products.length==0">
|
<div *ngIf="products.length==0">
|
||||||
No Products found!
|
No Products found!
|
||||||
</div>
|
</div>
|
||||||
<div class="productItem" *ngFor="let product of products" (click)="clickedProduct(product)">
|
<div class="container mt-5 mb-5">
|
||||||
<div class="productImageContainer" *ngIf="showProductPicture===true">
|
<div class="d-flex justify-content-center row">
|
||||||
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>
|
<div class="col-md-10">
|
||||||
</div>
|
<div class="row p-2 bg-white border rounded" *ngFor="let product of products">
|
||||||
<div class="productTitle">
|
<div class="col-md-3 mt-1"><img width="50%" class="img-fluid img-responsive rounded product-image"
|
||||||
<b>{{product.name}}</b>
|
src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg">
|
||||||
</div>
|
</div>
|
||||||
<div class="productPrice">
|
<div class="col-md-6 mt-1">
|
||||||
5€
|
<h5>{{product.name}}</h5>
|
||||||
</div>
|
<div class="d-flex flex-row">
|
||||||
<div class="productDescription">
|
<p class="text-justify text-truncate para mb-0">{{product.short_description}}</p>
|
||||||
<div *ngIf="product.short_description.length > 300">
|
</div>
|
||||||
{{product.short_description.substring(0, 300) + "..."}}
|
<div class="mt-1 mb-1 spec-1"><span></span><span class="dot"></span><span></span><span
|
||||||
</div>
|
class="dot"></span><span><br></span></div>
|
||||||
<div *ngIf="product.short_description.length <= 300">
|
<div class="mt-1 mb-1 spec-1"><span></span><span class="dot"></span><span></span><span
|
||||||
{{product.short_description}}
|
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>
|
</div>
|
||||||
</div>
|
<!--
|
||||||
|
<div class="productItem" *ngFor="let product of products" (click)="clickedProduct(product)">
|
||||||
|
<div class="productImageContainer" *ngIf="showProductPicture===true">
|
||||||
|
<img class="productImage" src="https://www.mueller-patrick.tech/betterzon/images/{{product.image_guid}}.jpg"/>
|
||||||
|
</div>
|
||||||
|
<div class="productTitle">
|
||||||
|
<b>{{product.name}}</b>
|
||||||
|
</div>
|
||||||
|
<div class="productPrice">
|
||||||
|
5€
|
||||||
|
</div>
|
||||||
|
<div class="productDescription">
|
||||||
|
<div *ngIf="product.short_description.length > 300">
|
||||||
|
{{product.short_description.substring(0, 300) + "..."}}
|
||||||
|
</div>
|
||||||
|
<div *ngIf="product.short_description.length <= 300">
|
||||||
|
{{product.short_description}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ describe('ProductListComponent', () => {
|
|||||||
last_modified: new Date(),
|
last_modified: new Date(),
|
||||||
manufacturer_id: 1,
|
manufacturer_id: 1,
|
||||||
selling_rank: '1',
|
selling_rank: '1',
|
||||||
category_id: 1
|
category_id: 1,
|
||||||
|
price: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
component.clickedProduct(product);
|
component.clickedProduct(product);
|
||||||
|
|||||||
@@ -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)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-18
@@ -1,25 +1,25 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { SliderForProductsComponent } from './slider-for-products.component';
|
import {SliderForProductsComponent} from './slider-for-products.component';
|
||||||
|
|
||||||
describe('SliderForProductsComponent', () => {
|
describe('SliderForProductsComponent', () => {
|
||||||
let component: SliderForProductsComponent;
|
let component: SliderForProductsComponent;
|
||||||
let fixture: ComponentFixture<SliderForProductsComponent>;
|
let fixture: ComponentFixture<SliderForProductsComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ SliderForProductsComponent ]
|
declarations: [SliderForProductsComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(SliderForProductsComponent);
|
fixture = TestBed.createComponent(SliderForProductsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-slider-for-products',
|
selector: 'app-slider-for-products',
|
||||||
templateUrl: './slider-for-products.component.html',
|
templateUrl: './slider-for-products.component.html',
|
||||||
styleUrls: ['./slider-for-products.component.css']
|
styleUrls: ['./slider-for-products.component.css']
|
||||||
})
|
})
|
||||||
export class SliderForProductsComponent implements OnInit {
|
export class SliderForProductsComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
<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"
|
||||||
</form>
|
(keyup.enter)="getSearchedProducts()" [(ngModel)]="searchQuery">
|
||||||
<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">
|
</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">
|
||||||
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-offers</a>
|
||||||
<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>
|
||||||
<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="#about">about</a>
|
||||||
<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>
|
||||||
<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"><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" *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" *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,25 +1,48 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { TopBarComponent } from './top-bar.component';
|
import {TopBarComponent} from './top-bar.component';
|
||||||
|
import {FormBuilder} from '@angular/forms';
|
||||||
|
import {ApiService} from '../../services/api.service';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
import {AbstractMockObservableService} from '../../mocks/mock.service';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
getUserInfo(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSessionInfoFromLocalStorage(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('TopBarComponent', () => {
|
describe('TopBarComponent', () => {
|
||||||
let component: TopBarComponent;
|
let component: TopBarComponent;
|
||||||
let fixture: ComponentFixture<TopBarComponent>;
|
let fixture: ComponentFixture<TopBarComponent>;
|
||||||
|
let mockService;
|
||||||
|
const router = {
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
routerState: jasmine.createSpy('routerState')
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
mockService = new MockApiService();
|
||||||
declarations: [ TopBarComponent ]
|
await TestBed.configureTestingModule({
|
||||||
})
|
declarations: [TopBarComponent],
|
||||||
.compileComponents();
|
providers: [{provide: ApiService, useValue: mockService}, {provide: Router, useValue: router}]
|
||||||
});
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(TopBarComponent);
|
fixture = TestBed.createComponent(TopBarComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,52 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {ApiService} from '../../services/api.service';
|
||||||
|
import {NavigationEnd, Router} from '@angular/router';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-top-bar',
|
selector: 'app-top-bar',
|
||||||
templateUrl: './top-bar.component.html',
|
templateUrl: './top-bar.component.html',
|
||||||
styleUrls: ['./top-bar.component.css']
|
styleUrls: ['./top-bar.component.css']
|
||||||
})
|
})
|
||||||
export class TopBarComponent implements OnInit {
|
export class TopBarComponent implements OnInit {
|
||||||
|
|
||||||
sidenav: any;
|
sidenav: any;
|
||||||
|
isLoggedIn: boolean;
|
||||||
|
searchQuery: string;
|
||||||
|
|
||||||
constructor() { }
|
constructor(
|
||||||
|
private api: ApiService,
|
||||||
ngOnInit() {
|
private router: Router
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
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', '');
|
||||||
|
if (this.router.url === '/profile') {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,5 @@ export interface Product {
|
|||||||
manufacturer_id: number;
|
manufacturer_id: number;
|
||||||
selling_rank: string;
|
selling_rank: string;
|
||||||
category_id: number;
|
category_id: number;
|
||||||
|
price: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<app-header [showSearch]="true"></app-header>
|
<app-top-bar></app-top-bar>
|
||||||
<div id="imprint">
|
<div id="imprint" class="container masthead">
|
||||||
<h1>Impressum</h1>
|
<h1>Impressum</h1>
|
||||||
|
|
||||||
<h2>Angaben gemäß § 5 TMG</h2>
|
<h2>Angaben gemäß § 5 TMG</h2>
|
||||||
@@ -17,14 +17,16 @@
|
|||||||
<h3>Haftung für Inhalte</h3>
|
<h3>Haftung für Inhalte</h3>
|
||||||
<p>
|
<p>
|
||||||
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene
|
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
|
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
|
Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen
|
||||||
oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
|
oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben
|
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben
|
||||||
hiervon unberührt.
|
hiervon unberührt.
|
||||||
Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich.
|
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.
|
Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -44,16 +46,21 @@
|
|||||||
|
|
||||||
<h3>Urheberrecht</h3>
|
<h3>Urheberrecht</h3>
|
||||||
<p>
|
<p>
|
||||||
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht.
|
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
|
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
|
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.
|
Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet.
|
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter
|
||||||
Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung
|
beachtet.
|
||||||
aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir
|
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.
|
derartige Inhalte umgehend entfernen.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<app-footer></app-footer>
|
<app-bottom-bar></app-bottom-bar>
|
||||||
|
<app-copyright></app-copyright>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-imprint',
|
selector: 'app-imprint',
|
||||||
templateUrl: './imprint.component.html',
|
templateUrl: './imprint.component.html',
|
||||||
styleUrls: ['./imprint.component.css']
|
styleUrls: ['./imprint.component.css']
|
||||||
})
|
})
|
||||||
export class ImprintComponent implements OnInit {
|
export class ImprintComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,6 +9,7 @@ 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
|
||||||
@@ -15,6 +17,7 @@ export class LandingpageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startedSearch(): void {
|
startedSearch(): void {
|
||||||
@@ -25,5 +28,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,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-page-not-found-page',
|
selector: 'app-page-not-found-page',
|
||||||
templateUrl: './page-not-found-page.component.html',
|
templateUrl: './page-not-found-page.component.html',
|
||||||
styleUrls: ['./page-not-found-page.component.css']
|
styleUrls: ['./page-not-found-page.component.css']
|
||||||
})
|
})
|
||||||
export class PageNotFoundPageComponent implements OnInit {
|
export class PageNotFoundPageComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<app-header [showSearch]="true"></app-header>
|
<app-top-bar></app-top-bar>
|
||||||
<div id="privacy">
|
<div id="privacy" class="container masthead">
|
||||||
<h1>Datenschutz­erklärung</h1>
|
<h1>Datenschutz­erklärung</h1>
|
||||||
<h2>1. Datenschutz auf einen Blick</h2>
|
<h2>1. Datenschutz auf einen Blick</h2>
|
||||||
<h3>Allgemeine Hinweise</h3>
|
<h3>Allgemeine Hinweise</h3>
|
||||||
<p>
|
<p>
|
||||||
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren
|
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
|
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
|
Sie persönlich identifiziert werden können. Ausführliche Informationen zum Thema Datenschutz
|
||||||
entnehmen Sie unserer unter diesem Text aufgeführten Datenschutzerklärung.
|
entnehmen Sie unserer unter diesem Text aufgeführten Datenschutzerklärung.
|
||||||
</p>
|
</p>
|
||||||
@@ -40,8 +41,10 @@
|
|||||||
Sie haben jederzeit das Recht, unentgeltlich Auskunft über
|
Sie haben jederzeit das Recht, unentgeltlich Auskunft über
|
||||||
Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem
|
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
|
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
|
Datenverarbeitung erteilt haben, können Sie diese Einwilligung jederzeit für die Zukunft widerrufen.
|
||||||
haben Sie das Recht, unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen
|
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
|
Daten zu verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde
|
||||||
zu.
|
zu.
|
||||||
</p>
|
</p>
|
||||||
@@ -85,13 +88,15 @@
|
|||||||
<h3>Datenschutz</h3>
|
<h3>Datenschutz</h3>
|
||||||
<p>
|
<p>
|
||||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir
|
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
|
behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften
|
||||||
|
sowie
|
||||||
dieser Datenschutzerklärung.
|
dieser Datenschutzerklärung.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten
|
Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten
|
||||||
erhoben. Personenbezogene Daten sind Daten, mit denen Sie persönlich identifiziert werden können. Die
|
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
|
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.
|
erläutert auch, wie und zu welchem Zweck das geschieht.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -115,7 +120,8 @@
|
|||||||
E-Mail: betterzon-privacy@mueller-patrick.tech
|
E-Mail: betterzon-privacy@mueller-patrick.tech
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Verantwortliche Stelle ist die natürliche oder juristische Person, die allein oder gemeinsam mit anderen über
|
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. Ä.)
|
die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z. B. Namen, E-Mail-Adressen o. Ä.)
|
||||||
entscheidet.
|
entscheidet.
|
||||||
</p>
|
</p>
|
||||||
@@ -124,7 +130,8 @@
|
|||||||
<p>
|
<p>
|
||||||
Soweit innerhalb dieser Datenschutzerklärung keine speziellere Speicherdauer genannt
|
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.
|
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,
|
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
|
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
|
Speicherung Ihrer personenbezogenen Daten haben (z.B. steuer- oder handelsrechtliche Aufbewahrungsfristen); im
|
||||||
letztgenannten Fall erfolgt die Löschung nach Fortfall dieser Gründe.
|
letztgenannten Fall erfolgt die Löschung nach Fortfall dieser Gründe.
|
||||||
@@ -139,7 +146,8 @@
|
|||||||
Datenschutzniveau garantiert werden kann. Beispielsweise sind US-Unternehmen dazu verpflichtet, personenbezogene
|
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.
|
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
|
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
|
befindlichen Daten zu Überwachungszwecken verarbeiten, auswerten und dauerhaft speichern. Wir haben auf
|
||||||
|
diese
|
||||||
Verarbeitungstätigkeiten keinen Einfluss.
|
Verarbeitungstätigkeiten keinen Einfluss.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -150,15 +158,19 @@
|
|||||||
erfolgten Datenverarbeitung bleibt vom Widerruf unberührt.
|
erfolgten Datenverarbeitung bleibt vom Widerruf unberührt.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21 DSGVO)</h3>
|
<h3>Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21
|
||||||
|
DSGVO)</h3>
|
||||||
<p>
|
<p>
|
||||||
WENN DIE DATENVERARBEITUNG AUF GRUNDLAGE VON ART. 6 ABS. 1 LIT. E ODER F DSGVO ERFOLGT, HABEN SIE JEDERZEIT DAS
|
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
|
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
|
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.
|
PROFILING. DIE JEWEILIGE RECHTSGRUNDLAGE, AUF DENEN EINE VERARBEITUNG BERUHT, ENTNEHMEN SIE DIESER
|
||||||
WENN SIE WIDERSPRUCH EINLEGEN, WERDEN WIR IHRE BETROFFENEN PERSONENBEZOGENEN DATEN NICHT MEHR VERARBEITEN, ES SEI
|
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
|
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
|
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).
|
VERTEIDIGUNG VON RECHTSANSPRÜCHEN (WIDERSPRUCH NACH ART. 21 ABS. 1 DSGVO).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -166,7 +178,8 @@
|
|||||||
DATEN VERARBEITET, UM DIREKTWERBUNG ZU BETREIBEN, SO HABEN SIE DAS RECHT, JEDERZEIT WIDERSPRUCH GEGEN DIE
|
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
|
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
|
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
|
PERSONENBEZOGENEN DATEN ANSCHLIESSEND NICHT MEHR ZUM ZWECKE DER DIREKTWERBUNG VERWENDET (WIDERSPRUCH NACH ART.
|
||||||
|
21
|
||||||
ABS. 2 DSGVO).
|
ABS. 2 DSGVO).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -174,7 +187,8 @@
|
|||||||
<p>
|
<p>
|
||||||
Im Falle von Verstößen
|
Im Falle von Verstößen
|
||||||
gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer Aufsichtsbehörde, insbesondere in dem
|
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
|
Mitgliedstaat ihres gewöhnlichen Aufenthalts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen
|
||||||
|
Verstoßes
|
||||||
zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher
|
zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher
|
||||||
Rechtsbehelfe.
|
Rechtsbehelfe.
|
||||||
</p>
|
</p>
|
||||||
@@ -182,7 +196,8 @@
|
|||||||
<h3>Recht auf Daten­übertrag­barkeit</h3>
|
<h3>Recht auf Daten­übertrag­barkeit</h3>
|
||||||
<p>
|
<p>
|
||||||
Sie haben das Recht, Daten, die wir auf Grundlage Ihrer
|
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
|
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
|
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.
|
Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.
|
||||||
</p>
|
</p>
|
||||||
@@ -190,8 +205,10 @@
|
|||||||
<h3>SSL- bzw. TLS-Verschlüsselung</h3>
|
<h3>SSL- bzw. TLS-Verschlüsselung</h3>
|
||||||
<p>
|
<p>
|
||||||
Diese Seite nutzt aus Sicherheitsgründen und zum Schutz der Übertragung
|
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
|
vertraulicher Inhalte, wie zum Beispiel Bestellungen oder Anfragen, die Sie an uns als Seitenbetreiber senden,
|
||||||
SSL- bzw. TLS-Verschlüsselung. Eine verschlüsselte Verbindung erkennen Sie daran, dass die Adresszeile des
|
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
|
Browsers von „http://“ auf „https://“ wechselt und an dem Schloss-Symbol in Ihrer
|
||||||
Browserzeile.
|
Browserzeile.
|
||||||
</p>
|
</p>
|
||||||
@@ -203,8 +220,10 @@
|
|||||||
<h3>Auskunft, Löschung und Berichtigung</h3>
|
<h3>Auskunft, Löschung und Berichtigung</h3>
|
||||||
<p>
|
<p>
|
||||||
Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen
|
Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen
|
||||||
jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen Daten, deren Herkunft
|
jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen Daten, deren
|
||||||
und Empfänger und den Zweck der Datenverarbeitung und ggf. ein Recht auf Berichtigung oder Löschung dieser
|
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
|
Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene Daten können Sie sich jederzeit an uns
|
||||||
wenden.
|
wenden.
|
||||||
</p>
|
</p>
|
||||||
@@ -216,22 +235,29 @@
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir in
|
Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir
|
||||||
der Regel Zeit, um dies zu überprüfen. Für die Dauer der Prüfung haben Sie das Recht, die
|
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.
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können Sie
|
Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können
|
||||||
|
Sie
|
||||||
statt der Löschung die Einschränkung der Datenverarbeitung verlangen.
|
statt der Löschung die Einschränkung der Datenverarbeitung verlangen.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Wenn wir Ihre personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung, Verteidigung
|
Wenn wir Ihre personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung,
|
||||||
oder Geltendmachung von Rechtsansprüchen benötigen, haben Sie das Recht, statt der Löschung die
|
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.
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren und
|
Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren
|
||||||
unseren Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen überwiegen, haben
|
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.
|
Sie das Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -239,7 +265,8 @@
|
|||||||
Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschränkt haben, dürfen diese Daten
|
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
|
– 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
|
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
|
juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der Europäischen
|
||||||
|
Union
|
||||||
oder eines Mitgliedstaats verarbeitet werden.
|
oder eines Mitgliedstaats verarbeitet werden.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -250,12 +277,14 @@
|
|||||||
und richten auf Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer
|
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.
|
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
|
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
|
Endgerät gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch
|
||||||
|
Ihren
|
||||||
Webbrowser erfolgt.
|
Webbrowser erfolgt.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Teilweise können auch Cookies von Drittunternehmen auf Ihrem Endgerät
|
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
|
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
|
Nutzung bestimmter Dienstleistungen des Drittunternehmens (z.B. Cookies zur Abwicklung von
|
||||||
Zahlungsdienstleistungen).
|
Zahlungsdienstleistungen).
|
||||||
</p>
|
</p>
|
||||||
@@ -267,24 +296,31 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Cookies, die zur Durchführung des elektronischen Kommunikationsvorgangs (notwendige Cookies)
|
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
|
oder zur Bereitstellung bestimmter, von Ihnen erwünschter Funktionen (funktionale Cookies, z. B. für
|
||||||
Warenkorbfunktion) oder zur Optimierung der Website (z.B. Cookies zur Messung des Webpublikums) erforderlich sind,
|
die
|
||||||
werden auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO gespeichert, sofern keine andere Rechtsgrundlage angegeben wird.
|
Warenkorbfunktion) oder zur Optimierung der Website (z.B. Cookies zur Messung des Webpublikums) erforderlich
|
||||||
Der Websitebetreiber hat ein berechtigtes Interesse an der Speicherung von Cookies zur technisch fehlerfreien und
|
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,
|
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.
|
erfolgt die Speicherung der betreffenden Cookies ausschließlich auf Grundlage dieser Einwilligung (Art. 6
|
||||||
|
Abs.
|
||||||
1 lit. a DSGVO); die Einwilligung ist jederzeit widerrufbar.
|
1 lit. a DSGVO); die Einwilligung ist jederzeit widerrufbar.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Sie können Ihren Browser so einstellen,
|
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
|
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 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
|
Cookies beim Schließen des Browsers aktivieren. Bei der Deaktivierung von Cookies kann die Funktionalität
|
||||||
dieser Website eingeschränkt sein.
|
dieser Website eingeschränkt sein.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Soweit Cookies von Drittunternehmen oder zu Analysezwecken eingesetzt
|
Soweit Cookies von Drittunternehmen oder zu Analysezwecken eingesetzt
|
||||||
werden, werden wir Sie hierüber im Rahmen dieser Datenschutzerklärung gesondert informieren und ggf. eine
|
werden, werden wir Sie hierüber im Rahmen dieser Datenschutzerklärung gesondert informieren und ggf.
|
||||||
|
eine
|
||||||
Einwilligung abfragen.
|
Einwilligung abfragen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -318,10 +354,13 @@
|
|||||||
den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
|
den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erfüllung
|
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der
|
||||||
eines Vertrags zusammenhängt oder zur Durchführung vorvertraglicher Maßnahmen erforderlich ist. In
|
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
|
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
|
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.
|
lit. a DSGVO) sofern diese abgefragt wurde.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -335,21 +374,25 @@
|
|||||||
<p>
|
<p>
|
||||||
Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird
|
Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird
|
||||||
Ihre Anfrage inklusive aller daraus hervorgehenden personenbezogenen Daten (Name, Anfrage) zum Zwecke der
|
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
|
Bearbeitung Ihres Anliegens bei uns gespeichert und verarbeitet. Diese Daten geben wir nicht ohne Ihre
|
||||||
|
Einwilligung
|
||||||
weiter.
|
weiter.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre
|
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
|
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
|
erforderlich ist. In allen übrigen Fällen beruht die Verarbeitung auf unserem berechtigten Interesse
|
||||||
der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung
|
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.
|
(Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Die von Ihnen an uns per Kontaktanfragen übersandten
|
Die von Ihnen an uns per Kontaktanfragen übersandten
|
||||||
Daten verbleiben bei uns, bis Sie uns zur Löschung auffordern, Ihre Einwilligung zur Speicherung widerrufen
|
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
|
oder der Zweck für die Datenspeicherung entfällt (z. B. nach abgeschlossener Bearbeitung Ihres
|
||||||
Anliegens). Zwingende gesetzliche Bestimmungen – insbesondere gesetzliche Aufbewahrungsfristen – bleiben
|
Anliegens). Zwingende gesetzliche Bestimmungen – insbesondere gesetzliche Aufbewahrungsfristen –
|
||||||
|
bleiben
|
||||||
unberührt.
|
unberührt.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -363,10 +406,13 @@
|
|||||||
<p>
|
<p>
|
||||||
Zu diesem Zweck muss der von Ihnen
|
Zu diesem Zweck muss der von Ihnen
|
||||||
verwendete Browser Verbindung zu den Servern von Google aufnehmen. Hierdurch erlangt Google Kenntnis darüber,
|
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
|
dass über Ihre IP-Adresse diese Website aufgerufen wurde. Die Nutzung von Google WebFonts erfolgt auf
|
||||||
von Art. 6 Abs. 1 lit. f DSGVO. Der Websitebetreiber hat ein berechtigtes Interesse an der einheitlichen Darstellung
|
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
|
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
|
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.
|
Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit widerrufbar.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -376,7 +422,8 @@
|
|||||||
<p>
|
<p>
|
||||||
Weitere Informationen zu Google Web Fonts finden Sie
|
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>
|
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"
|
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>.
|
rel="noopener noreferrer">https://policies.google.com/privacy?hl=de</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -388,10 +435,12 @@
|
|||||||
<p>
|
<p>
|
||||||
Beim Aufruf
|
Beim Aufruf
|
||||||
einer Seite lädt Ihr Browser die benötigten Fonts in ihren Browsercache, um Texte, Schriftarten und
|
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
|
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
|
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
|
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
|
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
|
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
|
Verarbeitung ausschließlich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO; die Einwilligung ist jederzeit
|
||||||
widerrufbar.
|
widerrufbar.
|
||||||
@@ -405,4 +454,5 @@
|
|||||||
von Font Awesome unter: <a href="https://fontawesome.com/privacy" target="_blank" rel="noopener noreferrer">https://fontawesome.com/privacy</a>.
|
von Font Awesome unter: <a href="https://fontawesome.com/privacy" target="_blank" rel="noopener noreferrer">https://fontawesome.com/privacy</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<app-footer></app-footer>
|
<app-bottom-bar></app-bottom-bar>
|
||||||
|
<app-copyright></app-copyright>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-privacy',
|
selector: 'app-privacy',
|
||||||
templateUrl: './privacy.component.html',
|
templateUrl: './privacy.component.html',
|
||||||
styleUrls: ['./privacy.component.css']
|
styleUrls: ['./privacy.component.css']
|
||||||
})
|
})
|
||||||
export class PrivacyComponent implements OnInit {
|
export class PrivacyComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<app-top-bar></app-top-bar>
|
<app-top-bar></app-top-bar>
|
||||||
|
|
||||||
<div id="mainComponents">
|
<div id="mainComponents">
|
||||||
<app-product-details [productId]="productId"></app-product-details>
|
<app-product-details [productId]="productId"></app-product-details>
|
||||||
<app-newest-prices-list [productId]="productId"></app-newest-prices-list>
|
<app-newest-prices-list [productId]="productId"></app-newest-prices-list>
|
||||||
</div>
|
</div>
|
||||||
|
<app-bottom-bar></app-bottom-bar>
|
||||||
|
|
||||||
|
|||||||
@@ -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,16 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
width: 5%;
|
||||||
|
height: 5%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<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/pencil.png">
|
||||||
|
</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,47 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ProfileComponent} from './profile.component';
|
||||||
|
import {AbstractMockObservableService} from '../../mocks/mock.service';
|
||||||
|
import {ApiService} from '../../services/api.service';
|
||||||
|
|
||||||
|
class MockApiService extends AbstractMockObservableService {
|
||||||
|
getUserInfo(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPriceAlarms(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProductsByIds(): any {
|
||||||
|
this.content = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ProfileComponent', () => {
|
||||||
|
let component: ProfileComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileComponent>;
|
||||||
|
let mockService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
mockService = new MockApiService();
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ProfileComponent],
|
||||||
|
providers: [{provide: ApiService, useValue: mockService}]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
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 {
|
||||||
|
const 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,7 +1,7 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import {ApiService} from './api.service';
|
import {ApiService} from './api.service';
|
||||||
import {HttpClientModule} from "@angular/common/http";
|
import {HttpClientModule} from '@angular/common/http';
|
||||||
|
|
||||||
describe('ApiService', () => {
|
describe('ApiService', () => {
|
||||||
let service: ApiService;
|
let service: ApiService;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {ContactPerson} from '../models/contactperson';
|
|||||||
import {Category} from '../models/category';
|
import {Category} from '../models/category';
|
||||||
import {Manufacturer} from '../models/manufacturer';
|
import {Manufacturer} from '../models/manufacturer';
|
||||||
import {CrawlingStatus} from '../models/crawlingstatus';
|
import {CrawlingStatus} from '../models/crawlingstatus';
|
||||||
|
import {log} from 'util';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -105,7 +106,7 @@ export class ApiService {
|
|||||||
let asin = '';
|
let asin = '';
|
||||||
|
|
||||||
// Check if the parameter is a link or an asin
|
// Check if the parameter is a link or an asin
|
||||||
const linkRegex: RegExp = /^http[s]{0,1}:\/\/.*\/dp\/(.[^\/]*)\/{0,1}.*$/;
|
const linkRegex: RegExp = /^http[s]?:\/\/.*\/dp\/(.[^\/]*)\/?.*$/;
|
||||||
const matches = linkRegex.exec(asinOrLink);
|
const matches = linkRegex.exec(asinOrLink);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
// param is a link, extract asin
|
// param is a link, extract asin
|
||||||
@@ -242,7 +243,11 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
addNewPrice(vendorId: number, productId: number, price: number): Observable<any> {
|
addNewPrice(vendorId: number, productId: number, price: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
return this.http.post((this.apiUrl + '/prices'), JSON.stringify({
|
return this.http.post((this.apiUrl + '/prices'), JSON.stringify({
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
vendor_id: vendorId,
|
vendor_id: vendorId,
|
||||||
product_id: productId,
|
product_id: productId,
|
||||||
price_in_cents: price
|
price_in_cents: price
|
||||||
@@ -277,7 +282,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
getManagedVendors(): Observable<Vendor[]> {
|
getManagedVendors(): Observable<Vendor[]> {
|
||||||
try {
|
try {
|
||||||
return this.http.get<Vendor[]>((this.apiUrl + '/vendors/managed'));
|
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.get<Vendor[]>((this.apiUrl + '/vendors/managed'), {params});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -317,10 +328,14 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
deactivateSingleVendorListing(vendorId: number, productId: number): Observable<any> {
|
deactivateSingleVendorListing(vendorId: number, productId: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.put((this.apiUrl + '/vendors/manage/deactivatelisting'), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.put((this.apiUrl + '/vendors/manage/deactivatelisting'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
vendor_id: vendorId,
|
vendor_id: vendorId,
|
||||||
product_id: productId
|
product_id: productId
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -333,7 +348,12 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
deactivateVendor(vendorId: number): Observable<any> {
|
deactivateVendor(vendorId: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.put((this.apiUrl + '/vendors/manage/shop/deactivate/' + vendorId), JSON.stringify({}));
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.put((this.apiUrl + '/vendors/manage/shop/deactivate/' + vendorId), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -346,7 +366,12 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
activateVendor(vendorId: number): Observable<any> {
|
activateVendor(vendorId: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.put((this.apiUrl + '/vendors/manage/shop/activate/' + vendorId), JSON.stringify({}));
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.put((this.apiUrl + '/vendors/manage/shop/activate/' + vendorId), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -366,7 +391,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
getPriceAlarms(): Observable<PriceAlarm[]> {
|
getPriceAlarms(): Observable<PriceAlarm[]> {
|
||||||
try {
|
try {
|
||||||
return this.http.get<PriceAlarm[]>((this.apiUrl + '/pricealarms'));
|
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.get<PriceAlarm[]>((this.apiUrl + '/pricealarms'), {params});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -380,10 +411,14 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
createPriceAlarms(productId: number, definedPrice: number): Observable<any> {
|
createPriceAlarms(productId: number, definedPrice: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.post((this.apiUrl + '/pricealarms'), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.post((this.apiUrl + '/pricealarms'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
product_id: productId,
|
product_id: productId,
|
||||||
defined_price: definedPrice
|
defined_price: definedPrice
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -397,10 +432,33 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
updatePriceAlarms(alarmId: number, definedPrice: number): Observable<any> {
|
updatePriceAlarms(alarmId: number, definedPrice: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.put((this.apiUrl + '/pricealarms'), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.put((this.apiUrl + '/pricealarms'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
alarm_id: alarmId,
|
alarm_id: alarmId,
|
||||||
defined_price: definedPrice
|
defined_price: definedPrice
|
||||||
}));
|
});
|
||||||
|
} catch (exception) {
|
||||||
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -423,11 +481,11 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
registerUser(username: string, password: string, email: string): Observable<any> {
|
registerUser(username: string, password: string, email: string): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.post((this.apiUrl + '/users/register'), JSON.stringify({
|
return this.http.post((this.apiUrl + '/users/register'), {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
email
|
email
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -441,15 +499,52 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
loginUser(username: string, password: string): Observable<any> {
|
loginUser(username: string, password: string): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.post((this.apiUrl + '/users/login'), JSON.stringify({
|
return this.http.post((this.apiUrl + '/users/login'), {
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all required information about the currently logged in user. If the user is not logged in or the
|
||||||
|
* session is not valid anymore, a 401 will come back from the backend.
|
||||||
|
* @return Observable<any> The observable response of the api
|
||||||
|
*/
|
||||||
|
getUserInfo(): Observable<any> {
|
||||||
|
try {
|
||||||
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
return this.http.post((this.apiUrl + '/users/checkSessionValid'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key
|
||||||
|
});
|
||||||
|
} catch (exception) {
|
||||||
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets session id and session key from local storage
|
||||||
|
* @return any {session_id: '', session_key: ''}
|
||||||
|
*/
|
||||||
|
getSessionInfoFromLocalStorage(): any {
|
||||||
|
const session_id = localStorage.getItem('session_id') ?? '';
|
||||||
|
const session_key = localStorage.getItem('session_key') ?? '';
|
||||||
|
return {session_id, session_key};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts and saves the session data from an api response
|
||||||
|
* @param data The api response
|
||||||
|
*/
|
||||||
|
saveSessionInfoToLocalStorage(data: any): boolean {
|
||||||
|
localStorage.setItem('session_id', data.session_id);
|
||||||
|
localStorage.setItem('session_key', data.session_key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ______ _ __ __
|
/* ______ _ __ __
|
||||||
/ ____/___ __ ______ _____(_) /____ _____/ /_ ____ ____ _____
|
/ ____/___ __ ______ _____(_) /____ _____/ /_ ____ ____ _____
|
||||||
/ /_ / __ `/ | / / __ \/ ___/ / __/ _ \ / ___/ __ \/ __ \/ __ \/ ___/
|
/ /_ / __ `/ | / / __ \/ ___/ / __/ _ \ / ___/ __ \/ __ \/ __ \/ ___/
|
||||||
@@ -464,7 +559,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
getFavoriteShops(): Observable<FavoriteShop[]> {
|
getFavoriteShops(): Observable<FavoriteShop[]> {
|
||||||
try {
|
try {
|
||||||
return this.http.get<FavoriteShop[]>((this.apiUrl + '/favoriteshops'));
|
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.get<FavoriteShop[]>((this.apiUrl + '/favoriteshops'), {params});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -477,9 +578,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
addFavoriteShop(vendorId: number): Observable<any> {
|
addFavoriteShop(vendorId: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.post((this.apiUrl + '/favoriteshops'), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.post((this.apiUrl + '/favoriteshops'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
vendor_id: vendorId
|
vendor_id: vendorId
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -492,7 +597,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
deleteFavoriteShop(vendorId: number): Observable<any> {
|
deleteFavoriteShop(vendorId: number): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.delete((this.apiUrl + '/favoriteshops/' + vendorId));
|
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 + '/favoriteshops/' + vendorId), {params});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -556,14 +667,18 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
addContactPerson(vendorId: number, firstName: string, lastName: string, gender: string, email: string, phone: string): Observable<any> {
|
addContactPerson(vendorId: number, firstName: string, lastName: string, gender: string, email: string, phone: string): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.post((this.apiUrl + '/contactpersons'), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.post((this.apiUrl + '/contactpersons'), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
vendor_id: vendorId,
|
vendor_id: vendorId,
|
||||||
first_name: firstName,
|
first_name: firstName,
|
||||||
last_name: lastName,
|
last_name: lastName,
|
||||||
gender,
|
gender,
|
||||||
email,
|
email,
|
||||||
phone
|
phone
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -582,14 +697,18 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
updateContactPerson(contactId: number, vendorId: number, firstName: string, lastName: string, gender: string, email: string, phone: string): Observable<any> {
|
updateContactPerson(contactId: number, vendorId: number, firstName: string, lastName: string, gender: string, email: string, phone: string): Observable<any> {
|
||||||
try {
|
try {
|
||||||
return this.http.put((this.apiUrl + '/contactpersons/' + contactId), JSON.stringify({
|
const sessionInfo = this.getSessionInfoFromLocalStorage();
|
||||||
|
|
||||||
|
return this.http.put((this.apiUrl + '/contactpersons/' + contactId), {
|
||||||
|
session_id: sessionInfo.session_id,
|
||||||
|
session_key: sessionInfo.session_key,
|
||||||
vendor_id: vendorId,
|
vendor_id: vendorId,
|
||||||
first_name: firstName,
|
first_name: firstName,
|
||||||
last_name: lastName,
|
last_name: lastName,
|
||||||
gender,
|
gender,
|
||||||
email,
|
email,
|
||||||
phone
|
phone
|
||||||
}));
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
@@ -704,7 +823,13 @@ export class ApiService {
|
|||||||
*/
|
*/
|
||||||
getCurrentCrawlingStatus(): Observable<CrawlingStatus> {
|
getCurrentCrawlingStatus(): Observable<CrawlingStatus> {
|
||||||
try {
|
try {
|
||||||
return this.http.get<CrawlingStatus>((this.apiUrl + '/crawlingstatus'));
|
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.get<CrawlingStatus>((this.apiUrl + '/crawlingstatus'), {params});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
process.stderr.write(`ERROR received from ${this.apiUrl}: ${exception}\n`);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 239 B |
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -1,12 +1,12 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
import {enableProdMode} from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
import {AppModule} from './app/app.module';
|
||||||
import { environment } from './environments/environment';
|
import {environment} from './environments/environment';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
.catch(err => console.error(err));
|
.catch(err => console.error(err));
|
||||||
|
|||||||
+622
-2
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user