feature/basic_functions #7

Merged
Seil0 merged 30 commits from feature/basic_functions into master 2020-05-10 17:48:31 +02:00
49 changed files with 1329 additions and 866 deletions

2
.gitignore vendored
View File

@ -89,3 +89,5 @@ local.properties
# SQLite # SQLite
prototype/*.db prototype/*.db
prototype/images

View File

@ -1,4 +1,4 @@
./test.db e-commerce.db
./build ./build
./gradle ./gradle
./out ./out

View File

@ -0,0 +1,6 @@
INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent", "should_be_advertised")
VALUES ("McDonalds", "1", 7, 1);
Outdated
Review

"should_be_advertised" fehlt hier.

"should_be_advertised" fehlt hier.

@Hannes, your change

@Hannes, your change
Review

fixed it

fixed it
INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id")
VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL);

View File

@ -1,9 +1,9 @@
/* password is 123 */ /* password is 123 */
INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee")
VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "1", "1", "0"); VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1");
INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee")
VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1", "0", "0"); VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0");
INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee")
VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0", "0", "0"); VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "0");

View File

@ -0,0 +1,30 @@
package org.hso.ecommerce.action.booking;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
public class CreateBookingAction {
private Booking booking;
public CreateBookingAction(BookingAccountEntry source, BookingAccountEntry destination, BookingReason reason, int amountCent) {
booking = new Booking();
booking.reason = reason;
booking.amountCent = amountCent;
assert source != null || destination != null;
if (source != null) {
booking.source = source.copyAddAmount(-amountCent);
}
if (destination != null) {
booking.destination = destination.copyAddAmount(amountCent);
}
}
public Booking finish() {
return booking;
}
}

View File

@ -0,0 +1,195 @@
package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.entities.booking.*;
import org.hso.ecommerce.entities.shop.Address;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.shop.CustomerOrderPosition;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CreateOrderAction {
private User user;
Address destination;
private int expectedTotalGrossCent;
private int totalNetCent;
private int totalVatCent;
private PaymentMethod method;
private BookingAccountEntry latestUserBooking;
private BookingAccountEntry latestVatBooking;
private BookingAccountEntry latestMainBooking;
private List<OrderItem> orderItems = new ArrayList<>();
public CreateOrderAction(
User user,
int expectedTotalGrossCent,
Address destination,
PaymentMethod method,
BookingAccountEntry latestUserBooking,
BookingAccountEntry latestVatBooking,
BookingAccountEntry latestMainBooking
) {
this.user = user;
this.expectedTotalGrossCent = expectedTotalGrossCent;
this.destination = destination;
this.method = method;
this.latestUserBooking = latestUserBooking;
assert latestUserBooking.userAccount.id == user.id;
this.latestVatBooking = latestVatBooking;
assert latestVatBooking.isVATAccount;
this.latestMainBooking = latestMainBooking;
assert latestMainBooking.isMainAccount;
}
public void addArticle(Article article, int quantity, List<WarehouseBookingPositionSlotEntry> availableSlots) {
for (WarehouseBookingPositionSlotEntry slot : availableSlots) {
assert slot.article.id == article.id;
}
orderItems.add(new OrderItem(article, availableSlots, quantity));
totalNetCent += article.shopPricePerUnitNetCent * quantity;
totalVatCent += article.getVat() * quantity;
}
public Result finish() throws ArticleNotInStockException {
CustomerOrder order = createOrder();
CustomerPayment payment = createPayment();
List<Booking> bookingList = new ArrayList<>();
bookingList.add(new CreateBookingAction(latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish());
bookingList.add(new CreateBookingAction(null, latestUserBooking, new BookingReason(payment), order.totalGrossCent).finish());
bookingList.add(new CreateBookingAction(latestMainBooking, latestVatBooking, new BookingReason(order), order.totalVatCent).finish());
WarehouseBooking warehouseBooking = createWarehouseBooking(order);
return new Result(
order,
warehouseBooking,
bookingList
);
}
private WarehouseBooking createWarehouseBooking(CustomerOrder order) throws ArticleNotInStockException {
WarehouseBooking booking = new WarehouseBooking();
booking.created = new Timestamp(new Date().getTime());
booking.reason = new BookingReason(order);
for (OrderItem item : orderItems) {
int needed = item.quantity;
for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) {
int remove = Math.min(slot.newSumSlot, needed);
needed -= remove;
WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition();
bookingPosition.article = item.article;
bookingPosition.amount = -remove;
bookingPosition.slotEntry = slot.copyAddAmount(-remove);
bookingPosition.booking = booking;
booking.positions.add(bookingPosition);
if (needed == 0) {
break;
}
}
if (needed > 0) {
throw new ArticleNotInStockException(item.article);
}
}
return booking;
}
private CustomerPayment createPayment() {
CustomerPayment payment = new CustomerPayment();
payment.amountCent = totalNetCent + totalVatCent;
payment.payment = method;
return payment;
}
private CustomerOrder createOrder() {
assert totalNetCent + totalVatCent == expectedTotalGrossCent;
CustomerOrder customerOrder = new CustomerOrder();
customerOrder.customer = user;
customerOrder.destination = destination;
for (OrderItem item : orderItems) {
CustomerOrderPosition position = new CustomerOrderPosition();
position.article = item.article;
position.pricePerUnit = item.article.shopPricePerUnitNetCent;
position.quantity = item.quantity;
position.order = customerOrder;
customerOrder.positions.add(position);
}
customerOrder.created = new Timestamp(new Date().getTime());
customerOrder.totalNetCent = totalNetCent;
customerOrder.totalVatCent = totalVatCent;
customerOrder.totalGrossCent = totalNetCent + totalVatCent;
return customerOrder;
}
public static class Result {
public final CustomerOrder customerOrder;
public final WarehouseBooking warehouseBooking;
public final List<Booking> bookings;
Result(CustomerOrder customerOrder, WarehouseBooking warehouseBooking, List<Booking> bookings) {
this.customerOrder = customerOrder;
this.warehouseBooking = warehouseBooking;
this.bookings = bookings;
}
}
private static class OrderItem {
List<WarehouseBookingPositionSlotEntry> availableSlots;
int quantity;
Article article;
public OrderItem(Article article, List<WarehouseBookingPositionSlotEntry> availableSlots, int quantity) {
this.article = article;
this.availableSlots = availableSlots;
this.quantity = quantity;
}
}
public static class ArticleNotInStockException extends Exception {
private Article article;
public ArticleNotInStockException(Article article) {
super("The quantity of article '" + article.title + "' is not in stock.");
this.article = article;
}
public Article getArticle() {
return article;
}
}
}

View File

@ -0,0 +1,11 @@
package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.entities.shop.CustomerOrder;
public class EnableTrackingAction {
public static void addTrackingInfo(CustomerOrder customerOrder) {
// TODO:
customerOrder.trackingId = "555-NASE";
}
}

View File

@ -0,0 +1,19 @@
package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.entities.shop.Article;
import java.util.ArrayList;
import java.util.List;
public class GetRandomArticlesAction {
public static List<Article> getRandomArticles(int quantity, List<Article> advertisedArticles) {
List<Article> randomisedArticles = new ArrayList<Article>();
int loopcount = Math.min(quantity, advertisedArticles.size());
Outdated
Review

Replace with Math.min?

Replace with Math.min?
Review

fixed

fixed
for (int i = 0; i < loopcount; i++) {
int index = (int) (Math.random() * advertisedArticles.size());
randomisedArticles.add(advertisedArticles.remove(index));
}
return randomisedArticles;
}
}

View File

@ -1,7 +0,0 @@
package org.hso.ecommerce.action.somepackage;
public class DemoAction {
// TODO: remove me.
// mksubpackage
}

View File

@ -3,6 +3,7 @@ package org.hso.ecommerce.app;
import org.hso.ecommerce.components.ErrorDemoInterceptor; import org.hso.ecommerce.components.ErrorDemoInterceptor;
import org.hso.ecommerce.components.InfoDemoInterceptor; import org.hso.ecommerce.components.InfoDemoInterceptor;
import org.hso.ecommerce.components.LoginIntercepter; import org.hso.ecommerce.components.LoginIntercepter;
import org.hso.ecommerce.components.ShoppingCartInterceptor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -23,5 +24,7 @@ public class Config implements WebMvcConfigurer {
registry.addInterceptor(buildLoginIntercepter()); registry.addInterceptor(buildLoginIntercepter());
registry.addInterceptor(new ErrorDemoInterceptor()); registry.addInterceptor(new ErrorDemoInterceptor());
registry.addInterceptor(new InfoDemoInterceptor()); registry.addInterceptor(new InfoDemoInterceptor());
registry.addInterceptor(new ShoppingCartInterceptor());
} }
} }

View File

@ -1,10 +1,12 @@
package org.hso.ecommerce.app; package org.hso.ecommerce.app;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -22,11 +24,6 @@ public class RequestController {
static int notSoRandom = 0; static int notSoRandom = 0;
@GetMapping("/")
public String home() {
return "redirect:/shop/";
}
@GetMapping("/login") @GetMapping("/login")
public String login() { public String login() {
return "login"; return "login";
@ -43,7 +40,7 @@ public class RequestController {
String gto = (String) session.getAttribute("afterLogin"); String gto = (String) session.getAttribute("afterLogin");
Optional<User> user = userRepository.findByEmail(username); Optional<User> user = userRepository.findByEmail(username);
if (user.isEmpty()) { if (!user.isPresent()) {
request.setAttribute("error", "Email Adresse falsch."); request.setAttribute("error", "Email Adresse falsch.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login"; return "login";
@ -88,71 +85,11 @@ public class RequestController {
return "redirect:/"; return "redirect:/";
} }
@GetMapping("/shop/")
public String shop() {
return "shop/index";
}
@GetMapping("/shop/search") @GetMapping("/shop/search")
public String shopSearch() { public String shopSearch() {
return "shop/search"; return "shop/search";
} }
@GetMapping("/shop/checkout")
public String shopCheckout(HttpSession session, HttpServletRequest request) {
session.setAttribute("afterLogin", request.getRequestURI());
return "shop/checkout";
}
@PostMapping("/shop/checkoutFinish")
public String shopCheckoutFinish() {
return "shop/checkoutFinish";
}
@GetMapping("/shop/checkoutFinish")
public String shopCheckoutFinishGET() {
return "shop/checkoutFinish";
}
@GetMapping("/shop/articles/{id}")
public String shopArticlesById() {
return "shop/articles/id";
}
@PostMapping("/shop/articles/{id}")
public String shopArticlesByIdBuy(HttpSession session,
@RequestAttribute(value = "user", required = false) User customer,
@PathVariable("id") Integer id,
@RequestParam("fastcheckout") Boolean fastcheckout
) {
if (customer != null) {
if (!fastcheckout) {
return "shop/articles/post_add";
} else {
return "shop/checkout";
}
} else {
session.setAttribute("afterLogin", "/shop/articles/" + id);
return "redirect:/login";
}
}
@GetMapping("/about")
public String about() {
return "about";
}
@GetMapping("/terms")
public String terms() {
return "terms";
}
@GetMapping("/privacy")
public String privacy() {
return "privacy";
}
@GetMapping("/intern/") @GetMapping("/intern/")
public String intern() { public String intern() {
return "intern/index"; return "intern/index";

View File

@ -0,0 +1,43 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.entities.shop.ShoppingCart;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class ShoppingCartInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Review

Laut Intellij werden die Exceptions nie geworfen.

Laut Intellij werden die Exceptions nie geworfen.
Review

Ist wegen dem Interface, die Methode ist dort so deklariert. Daher würde ich das gerne so lassen, zwecks Konsistenz

Ist wegen dem Interface, die Methode ist dort so deklariert. Daher würde ich das gerne so lassen, zwecks Konsistenz
Review

Ok

Ok
HttpSession session = request.getSession();
Object shoppingCart = session.getAttribute("shoppingCart");
if (shoppingCart == null) {
shoppingCart = new ShoppingCart();
}
request.setAttribute("shoppingCart", shoppingCart);
return true;
}
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HttpSession session = request.getSession();
Object shoppingCart = request.getAttribute("shoppingCart");
session.setAttribute("shoppingCart", shoppingCart);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {
}
}

View File

@ -0,0 +1,32 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.entities.warehouse.Slot;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class SlotInitializer {
@Autowired
private final SlotRepository slotRepository = null;
// TODO: use values form cfg.
private final int NUM_SLOTS = 50;
@PostConstruct
public void init() {
for (int i = 1; i <= NUM_SLOTS; i++) {
if (!slotRepository.findBySlotNum(i).isPresent()) {
Slot slotAdded = new Slot();
slotAdded.slotNum = i;
slotRepository.save(slotAdded);
System.out.println("Added Slot " + i + " to DB");
}
}
}
}

View File

@ -1,8 +1,103 @@
package org.hso.ecommerce.controller.shop; package org.hso.ecommerce.controller.shop;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.hso.ecommerce.action.shop.GetRandomArticlesAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.shop.ShoppingCart;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@Controller @Controller
Outdated
Review

Unused Imports bitte entfernen.

Unused Imports bitte entfernen.
//@RequestMapping("...") @RequestMapping("/shop/articles")
public class ShopArticleController { public class ShopArticleController {
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@GetMapping("/{id}")
public String shopArticlesById(Model model,
@PathVariable("id") Long id,
HttpServletRequest request,
HttpServletResponse response
) {
Article article = articleRepository.findArticleById(id);
if (article == null) {
request.setAttribute("error", "Der Artikel wurde nicht gefunden.");
Review

Unnötige Leerzeilen entfernen.

Unnötige Leerzeilen entfernen.
Review

fixed

fixed
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error/404";
}
model.addAttribute("article", article);
//if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //TODO: use this as soon as warehouse works
if (true) {
model.addAttribute("inStock", true);
} else {
model.addAttribute("inStock", false);
}
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles());
model.addAttribute("commercialArticles", commercialArticles);
return "shop/articles/id";
}
@PostMapping("/{id}")
public String shopArticlesByIdBuy(HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
@RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart,
@PathVariable("id") Long id,
@RequestParam("quantity") Integer quantity,
@RequestParam(value = "set_amount", required = false) Boolean setAmount,
@RequestParam("fastcheckout") Boolean fastcheckout
) {
Article article = articleRepository.findArticleById(id);
if (article == null) {
request.setAttribute("error", "Der Artikel wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error/404";
}
if (setAmount != null && setAmount) {
shoppingCart.setArticleCount(article, quantity);
} else {
shoppingCart.addArticle(article, quantity);
}
if (!fastcheckout) {
return "shop/articles/post_add";
} else {
return "redirect:/shop/checkout";
}
}
@GetMapping("/{id}/image.jpg")
public void getImageAsByteArray(HttpServletRequest request,
HttpServletResponse response,
@PathVariable("id") Long id
) throws IOException {
Article article = articleRepository.findArticleById(id);
InputStream in = new FileInputStream(article.image.path);
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
IOUtils.copy(in, response.getOutputStream());
}
} }

View File

@ -1,8 +1,160 @@
package org.hso.ecommerce.controller.shop; package org.hso.ecommerce.controller.shop;
import org.hso.ecommerce.action.shop.CreateOrderAction;
import org.hso.ecommerce.action.shop.EnableTrackingAction;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.PaymentMethod;
import org.hso.ecommerce.entities.shop.Address;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.shop.ShoppingCart;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CustomerOderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.TreeMap;
@Controller @Controller
//@RequestMapping("...") @RequestMapping("/shop/")
public class ShopCheckoutController { public class ShopCheckoutController {
@Autowired
private final UserRepository userRepository = null;
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final BookingAccountEntryRepository bookingEntryRepository = null;
@Autowired
private final BookingRepository bookingRepository = null;
@Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null;
@Autowired
private final CustomerOderRepository customerOderRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null;
@GetMapping("/checkout")
public String shopCheckout(HttpSession session,
HttpServletRequest request,
@RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart) {
session.setAttribute("afterLogin", request.getRequestURI());
CheckoutListTotals totals = new CheckoutListTotals();
ArrayList<CheckoutListItem> items = new ArrayList<>();
for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) {
Article article = articleRepository.findById(item.getArticleId()).get();
totals.addItem(article, item.getAmount());
items.add(new CheckoutListItem(item.getAmount(), article));
}
request.setAttribute("checkoutItems", items);
request.setAttribute("checkoutTotals", totals);
return "shop/checkout";
}
public static class CheckoutListTotals {
public int net;
public TreeMap<Integer, Integer> vatAmounts = new TreeMap<>();
public int total;
void addItem(Article article, int amount) {
net += article.shopPricePerUnitNetCent * amount;
Integer vatPos = vatAmounts.getOrDefault(article.related.vatPercent, 0) + article.getVat() * amount;
vatAmounts.put(article.related.vatPercent, vatPos);
total += article.getPriceGross() * amount;
}
}
public static class CheckoutListItem {
public int amount;
public Article article;
public int total;
public CheckoutListItem(int amount, Article article) {
this.amount = amount;
this.article = article;
this.total = amount * article.getPriceGross();
}
}
@PostMapping("/checkoutFinish")
public String shopCheckoutFinish(
HttpSession session,
HttpServletRequest request,
HttpServletResponse response,
@RequestAttribute(value = "user") User user,
@RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart,
@RequestParam("address") String address,
@RequestParam("cardnumber") String cardnumber,
@RequestParam("shopping_cart_revision") Integer cartRevision,
@RequestParam("expected_total") Integer expectedPrice
) {
if (shoppingCart.getRevision() != cartRevision) {
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
response.setStatus(HttpServletResponse.SC_CONFLICT);
return "shop/checkout";
}
// Must be refetched for persitence.
user = userRepository.findById(user.id).get();
CreateOrderAction action = new CreateOrderAction(
user,
expectedPrice,
Address.fromString(address),
PaymentMethod.fromCreditCarNumber(cardnumber),
bookingEntryRepository.getByUser(user.id).orElse(BookingAccountEntry.newUser(user)),
bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()),
bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain())
);
for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) {
Article article = articleRepository.findById(item.getArticleId()).get();
action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article.id));
}
CreateOrderAction.Result result = null;
try {
result = action.finish();
EnableTrackingAction.addTrackingInfo(result.customerOrder);
customerOderRepository.save(result.customerOrder);
bookingRepository.saveAll(result.bookings);
warehouseBookingRepository.save(result.warehouseBooking);
shoppingCart.clear();
} catch (CreateOrderAction.ArticleNotInStockException e) {
request.setAttribute("error", "Der Artikel '" + e.getArticle().title + "' ist leider nicht mehr in ausreichender Menge verfügbar. Bitte passen Sie die Artikelmenge an.");
return shopCheckout(session, request, shoppingCart);
}
return "shop/checkoutFinish";
}
@GetMapping("/checkoutFinish")
public String shopCheckoutFinishGET() {
return "shop/checkoutFinish";
}
} }

View File

@ -1,8 +1,69 @@
package org.hso.ecommerce.controller.shop; package org.hso.ecommerce.controller.shop;
import org.hso.ecommerce.action.shop.GetRandomArticlesAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.repos.shop.ArticleRepository;
Outdated
Review

Unused Import.

Unused Import.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller @Controller
//@RequestMapping("...") @RequestMapping("/")
public class ShopIndexController { public class ShopIndexController {
@Autowired
private final ArticleRepository articleRepository = null;
@GetMapping("/")
public String home() {
return "redirect:/shop/";
}
@GetMapping("/shop/")
public String shop(Model model, HttpSession session) {
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles());
model.addAttribute("commercialArticles", commercialArticles);
boolean isLoggedIn = false;
boolean hasOrders = false;
if (session != null && session.getAttribute("userId") != null) {
long userId = (long) session.getAttribute("userId");
isLoggedIn = true;
List<Article> suggestedArticles = articleRepository.getOrderedArticles(userId);
suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0, 4) : suggestedArticles; //only latest 4 articles
if (suggestedArticles.size() > 0) {
model.addAttribute("suggestedArticles", suggestedArticles);
hasOrders = true;
}
}
model.addAttribute("isLoggedIn", isLoggedIn);
model.addAttribute("hasOrders", hasOrders);
return "shop/index";
}
@GetMapping("/about")
public String about() {
return "about";
}
@GetMapping("/terms")
public String terms() {
return "terms";
}
@GetMapping("/privacy")
public String privacy() {
return "privacy";
}
} }

View File

@ -14,12 +14,12 @@ public class Booking {
// always >= 0 // always >= 0
public int amountCent; public int amountCent;
@ManyToOne(optional = true) @ManyToOne(optional = true, cascade = CascadeType.ALL)
public BookingAccountEntry source; public BookingAccountEntry source;
@ManyToOne(optional = true) @ManyToOne(optional = true, cascade = CascadeType.ALL)
public BookingAccountEntry destination; public BookingAccountEntry destination;
@OneToOne(optional = false) @OneToOne(optional = false, cascade = CascadeType.ALL)
public BookingReason reason; public BookingReason reason;
} }

View File

@ -16,13 +16,49 @@ public class BookingAccountEntry {
public int newSumCent; public int newSumCent;
@ManyToOne(optional = true) @ManyToOne(optional = true, cascade = CascadeType.ALL)
public User userAccount; public User userAccount;
@ManyToOne(optional = true) @ManyToOne(optional = true, cascade = CascadeType.ALL)
public Supplier supplierAccount; public Supplier supplierAccount;
public boolean isMainAccount; public boolean isMainAccount;
public boolean isVATAccount; public boolean isVATAccount;
public BookingAccountEntry copyAddAmount(int amountCent) {
BookingAccountEntry e = new BookingAccountEntry();
e.userAccount = userAccount;
e.supplierAccount = supplierAccount;
e.isMainAccount = isMainAccount;
e.isVATAccount = isVATAccount;
e.newSumCent = newSumCent + amountCent;
return e;
}
public static BookingAccountEntry newUser(User user) {
BookingAccountEntry e = new BookingAccountEntry();
e.userAccount = user;
e.newSumCent = 0;
return e;
}
public static BookingAccountEntry newMain() {
BookingAccountEntry e = new BookingAccountEntry();
e.isMainAccount = true;
e.newSumCent = 0;
return e;
}
public static BookingAccountEntry newVat() {
BookingAccountEntry e = new BookingAccountEntry();
e.isVATAccount = true;
e.newSumCent = 0;
return e;
}
} }

View File

@ -22,9 +22,21 @@ public class BookingReason {
@ManyToOne(optional = true) @ManyToOne(optional = true)
public CustomerOrder customerOrder; public CustomerOrder customerOrder;
@ManyToOne(optional = true) @OneToOne(optional = true, cascade = CascadeType.ALL)
public CustomerPayment customerPayment; public CustomerPayment customerPayment;
@ManyToOne(optional = true) @ManyToOne(optional = true)
public SupplierOrder supplierOrder; public SupplierOrder supplierOrder;
// Default Constructor is needed for construction by ORM
public BookingReason() {
}
Review

Das kann auch weg.

Das kann auch weg.
Review

Nope, ORM braucht nen leeren Konstruktor. Ich mach ein Kommentar drüber. 713c8ebe86

Nope, ORM braucht nen leeren Konstruktor. Ich mach ein Kommentar drüber. 713c8ebe86faffcde94f076956bdc5d02fe3ddaa
Review

Ich meinte auch das Semikolon.

Ich meinte auch das Semikolon.
public BookingReason(CustomerOrder order) {
this.customerOrder = order;
}
public BookingReason(CustomerPayment customerPayment) {
this.customerPayment = customerPayment;
}
} }

View File

@ -7,4 +7,11 @@ import javax.validation.constraints.NotNull;
public class PaymentMethod { public class PaymentMethod {
@NotNull @NotNull
public String creditCardNumber; public String creditCardNumber;
public static PaymentMethod fromCreditCarNumber(String cardnumber) {
PaymentMethod m = new PaymentMethod();
m.creditCardNumber = cardnumber;
return m;
}
} }

View File

@ -4,7 +4,24 @@ import javax.persistence.Embeddable;
@Embeddable @Embeddable
public class Address { public class Address {
public String name; public String name = "";
public String addressString; public String addressString = "";
public String country = "DE"; public String country = "DE";
@Override
public String toString() {
return name + "\n" + addressString;
}
public static Address fromString(String addr) {
Address a = new Address();
String[] arr = addr.split("\n", 2);
a.name = arr[0];
if (arr.length > 1) {
a.addressString = arr[1];
}
return a;
}
} }

View File

@ -32,9 +32,18 @@ public class Article {
public String description; public String description;
@OneToOne(optional = true) @OneToOne(optional = true)
@Basic(fetch = FetchType.LAZY)
public Image image; public Image image;
@ManyToMany @ManyToMany
@JoinTable(name = "article_categories_bindings") @JoinTable(name = "article_categories_bindings")
public Set<Category> categories = new HashSet<>(); public Set<Category> categories = new HashSet<>();
public int getVat() {
return (shopPricePerUnitNetCent * related.vatPercent) / 100;
}
public int getPriceGross() {
return shopPricePerUnitNetCent + getVat();
}
} }

View File

@ -24,7 +24,7 @@ public class CustomerOrder {
@OneToMany( @OneToMany(
targetEntity = CustomerOrderPosition.class, targetEntity = CustomerOrderPosition.class,
mappedBy = "order" mappedBy = "order", cascade = CascadeType.ALL
) )
public List<CustomerOrderPosition> positions = new ArrayList<>(); public List<CustomerOrderPosition> positions = new ArrayList<>();

View File

@ -11,7 +11,5 @@ public class Image {
@Basic @Basic
public long id; public long id;
@Lob public String path;
@Column(name = "data", columnDefinition = "BLOB", nullable = false)
private byte[] data;
} }

View File

@ -0,0 +1,103 @@
package org.hso.ecommerce.entities.shop;
import java.util.ArrayList;
import java.util.List;
// Not a db entity. Just for session storage
public class ShoppingCart {
private final static int MAX_ITEMS = 10;
private int revision;
private ArrayList<ShoppingCartItem> items;
public ShoppingCart() {
clear();
}
public void clear() {
items = new ArrayList<>();
revision = (int) Math.round(Math.random() * 0xFFFF);
}
public List<ShoppingCartItem> getItems() {
return items;
}
public int getItemCount() {
int count = 0;
for (ShoppingCartItem i : items) {
count += i.getAmount();
}
return count;
}
public int getRevision() {
return revision;
}
public void addArticle(Article article, int quantity) {
this.revision++;
for (ShoppingCartItem i : items) {
if (i.getArticleId() == article.id) {
i.addAmount(quantity);
return;
}
}
items.add(new ShoppingCartItem(quantity, article));
}
public void setArticleCount(Article article, Integer quantity) {
this.revision++;
boolean found = false;
for (ShoppingCartItem i : items) {
if (i.getArticleId() == article.id) {
i.setAmount(quantity);
found = true;
break;
}
}
if (!found) {
items.add(new ShoppingCartItem(quantity, article));
}
items.removeIf(i -> i.getAmount() <= 0);
}
public static class ShoppingCartItem {
private int amount;
private final long articleId;
public ShoppingCartItem(int amount, Article article) {
this.amount = amount;
this.articleId = article.id;
}
public int getAmount() {
return amount;
}
public void addAmount(int amount) {
this.amount += amount;
if (this.amount > MAX_ITEMS) {
this.amount = MAX_ITEMS;
}
}
public long getArticleId() {
return articleId;
}
public void setAmount(Integer amount) {
this.amount = amount;
if (this.amount > MAX_ITEMS) {
this.amount = MAX_ITEMS;
}
}
}
}

View File

@ -19,4 +19,6 @@ public class ArticleOffer {
public String articleNumber; public String articleNumber;
public int vatPercent; public int vatPercent;
public boolean shouldBeAdvertised;
} }

View File

@ -30,10 +30,10 @@ public class User {
public boolean isEmployee; public boolean isEmployee;
@Embedded @Embedded
private Address defaultDeliveryAddress; public Address defaultDeliveryAddress;
@Embedded @Embedded
private PaymentMethod defaultPayment; public PaymentMethod defaultPayment;
public long getId() { public long getId() {
return id; return id;

View File

@ -0,0 +1,17 @@
package org.hso.ecommerce.entities.warehouse;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "warehouse_slots")
public class Slot {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic
public long id;
@NotNull
@Column(unique = true)
public int slotNum;
}

View File

@ -1,5 +1,7 @@
package org.hso.ecommerce.entities.warehouse; package org.hso.ecommerce.entities.warehouse;
import org.hso.ecommerce.entities.booking.BookingReason;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,7 +23,11 @@ public class WarehouseBooking {
public boolean isDone; public boolean isDone;
@OneToMany( @OneToMany(
mappedBy = "booking" mappedBy = "booking", cascade = CascadeType.ALL
) )
public List<WarehouseBookingPosition> positions = new ArrayList<>(); public List<WarehouseBookingPosition> positions = new ArrayList<>();
// TODO FIX ME
@OneToOne(optional = false, cascade = CascadeType.ALL)
public BookingReason reason;
} }

View File

@ -23,10 +23,6 @@ public class WarehouseBookingPosition {
public int amount; // positive or negative public int amount; // positive or negative
@ManyToOne(optional = true) @ManyToOne(optional = true, cascade = CascadeType.ALL)
public WarehouseBookingPositionSlotEntry source; public WarehouseBookingPositionSlotEntry slotEntry;
@ManyToOne(optional = true)
public WarehouseBookingPositionSlotEntry destination;
} }

View File

@ -3,6 +3,7 @@ package org.hso.ecommerce.entities.warehouse;
import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.Article;
import javax.persistence.*; import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity @Entity
@Table(name = "warehouse_booking_position_entries") @Table(name = "warehouse_booking_position_entries")
@ -13,11 +14,28 @@ public class WarehouseBookingPositionSlotEntry {
@Basic @Basic
public long id; public long id;
@NotNull
@ManyToOne @ManyToOne
public Article article; public Article article;
public int newSumArticles; @NotNull
Outdated
Review

; > '

; > '
Review

removed bd2aeb63f6

removed bd2aeb63f6ea5a3c0d2f78cde2ff8f5691061f30
public int newSumWarehousePosition; public int newSumSlot;
public int slot; @NotNull
@ManyToOne
public Slot slot;
public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) {
WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry();
e.article = article;
e.slot = slot;
e.newSumSlot = newSumSlot + amount;
assert e.article.warehouseUnitsPerSlot >= e.newSumSlot;
assert e.newSumSlot >= 0;
return e;
}
} }

View File

@ -23,4 +23,12 @@ public class WarehouseBookingReason {
public CustomerOrder customerOrder; public CustomerOrder customerOrder;
public boolean isManuel; public boolean isManuel;
// Default Constructor is needed for construction by ORM
public WarehouseBookingReason() {
}
public WarehouseBookingReason(CustomerOrder order) {
this.customerOrder = order;
}
} }

View File

@ -0,0 +1,25 @@
package org.hso.ecommerce.repos.booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface BookingAccountEntryRepository extends JpaRepository<BookingAccountEntry, Long> {
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getByUser(Long user);
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getByMain();
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.isvataccount = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getByVat();
}

View File

@ -0,0 +1,12 @@
package org.hso.ecommerce.repos.booking;
import org.hso.ecommerce.entities.booking.Booking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookingRepository extends JpaRepository<Booking, Long> {
}

View File

@ -0,0 +1,27 @@
package org.hso.ecommerce.repos.shop;
import org.hso.ecommerce.entities.shop.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
@Query("SELECT a FROM Article a WHERE a.id = :articleId")
Article findArticleById(@Param("articleId") long articleId);
@Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.shouldBeAdvertised = true")
List<Article> getAdvertisedArticles();
@Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a WHERE c.id = :customerId ORDER BY co.id DESC")
List<Article> getOrderedArticles(long customerId);
}

View File

@ -0,0 +1,11 @@
package org.hso.ecommerce.repos.shop;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerOderRepository extends JpaRepository<CustomerOrder, Long> {
}

View File

@ -0,0 +1,17 @@
package org.hso.ecommerce.repos.warehouse;
import org.hso.ecommerce.entities.warehouse.Slot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface SlotRepository extends JpaRepository<Slot, Long> {
@Query("SELECT s FROM Slot s WHERE s.slotNum = :slotNum")
Optional<Slot> findBySlotNum(int slotNum);
}

View File

@ -0,0 +1,17 @@
package org.hso.ecommerce.repos.warehouse;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository<WarehouseBookingPositionSlotEntry, Long> {
@Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true)
List<WarehouseBookingPositionSlotEntry> getByArticle(long article);
}

View File

@ -0,0 +1,11 @@
package org.hso.ecommerce.repos.warehouse;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface WarehouseBookingRepository extends JpaRepository<WarehouseBooking, Long> {
}

View File

@ -3,7 +3,7 @@ spring.resources.cache.cachecontrol.maxAge=P0D
# LOGGING # LOGGING
logging.level.org.springframework.web=WARN logging.level.org.springframework.web=WARN
# DATABASE # DATABASE
spring.datasource.url=jdbc:sqlite:./test.db spring.datasource.url=jdbc:sqlite:./e-commerce.db
spring.datasource.driverClassName=org.sqlite.JDBC spring.datasource.driverClassName=org.sqlite.JDBC
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLiteDialect spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLiteDialect
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update

View File

@ -1,7 +0,0 @@
CREATE TABLE "customers" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"lastname" TEXT,
"firstname" TEXT,
"username" TEXT,
"password" TEXT
);

View File

@ -1,277 +1,100 @@
<svg id="mainImage_create" data-name="mainImage" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 171.2 81.5"> <svg id="fd59ce54-f850-4dfc-bc34-dd7d379d600e" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1074.392"
<style id="svgStyle"> height="584.231" viewBox="0 0 1074.392 584.231">
@import url('https://fonts.googleapis.com/css?family=Merriweather'); <title>page not found</title>
.changeColor { <ellipse cx="540.64346" cy="549.3094" rx="527.5" ry="34.9216" fill="#f2f2f2"/>
fill: #4f86ed; <path d="M583.47969,324.89424c-85.94407,0-147.651,55.13938-147.651,183.79791,0,145.813,61.70691,184.41057,147.651,184.41057s151.327-42.27352,151.327-184.41057C734.80664,356.75255,669.42376,324.89424,583.47969,324.89424Zm.56495,319.80837c-59.52686,0-90.62592-34.92288-90.62592-135.9163,0-89.11185,32.37209-136.10461,91.899-136.10461s91.899,30.86774,91.899,136.10461C677.21663,607.23367,643.5715,644.70261,584.04464,644.70261Z"
} transform="translate(-63.054 -157.8845)" fill="#2f2e41"/>
<path d="M384.36531,591.40121H348.831V486.76183A20.95585,20.95585,0,0,0,327.87517,465.806h-8.32638a20.95585,20.95585,0,0,0-20.95586,20.95585V591.40121H198.36285a11.96327,11.96327,0,0,1-10.57763-17.552l106.0824-200.78034A20.95585,20.95585,0,0,0,284.28724,344.33l-6.26231-2.9572a20.95585,20.95585,0,0,0-27.4293,9.07005L121.21416,592.4754a28.41578,28.41578,0,0,0-3.35584,13.39612v0a28.41583,28.41583,0,0,0,28.41584,28.41583H298.59293v66.16727a25.119,25.119,0,0,0,25.119,25.119h.00005a25.119,25.119,0,0,0,25.119-25.119V634.28739h35.53428a21.44307,21.44307,0,0,0,21.44307-21.44307v0A21.44307,21.44307,0,0,0,384.36531,591.40121Z"
#title { transform="translate(-63.054 -157.8845)" fill="#16a085"/>
font-size: 50%; <path d="M1042.36183,591.40121h-35.53428V486.76183A20.95585,20.95585,0,0,0,985.87169,465.806h-8.32638a20.95585,20.95585,0,0,0-20.95586,20.95585V591.40121H856.35937a11.96326,11.96326,0,0,1-10.57763-17.552L951.86413,373.06891A20.95586,20.95586,0,0,0,942.28376,344.33l-6.26231-2.9572a20.95586,20.95586,0,0,0-27.42931,9.07005L779.21068,592.4754a28.41578,28.41578,0,0,0-3.35584,13.39612v0a28.41583,28.41583,0,0,0,28.41583,28.41583H956.58945v66.16727a25.119,25.119,0,0,0,25.119,25.119h0a25.119,25.119,0,0,0,25.119-25.119V634.28739h35.53428a21.44307,21.44307,0,0,0,21.44307-21.44307v0A21.44307,21.44307,0,0,0,1042.36183,591.40121Z"
font-family: 'Merriweather', serif; transform="translate(-63.054 -157.8845)" fill="#16a085"/>
} <path d="M394.16787,579.148H358.63358V474.50864a20.95585,20.95585,0,0,0-20.95585-20.95586h-8.32638a20.95586,20.95586,0,0,0-20.95586,20.95586V579.148H208.16541a11.96327,11.96327,0,0,1-10.57763-17.552L303.67017,360.81572a20.95586,20.95586,0,0,0-9.58037-28.73893l-6.26231-2.9572a20.95586,20.95586,0,0,0-27.42931,9.07L131.01672,580.2222a28.41582,28.41582,0,0,0-3.35584,13.39613v0a28.41583,28.41583,0,0,0,28.41583,28.41583H308.39549v66.16727a25.119,25.119,0,0,0,25.119,25.119h.00005a25.119,25.119,0,0,0,25.119-25.119V622.0342h35.53429a21.44307,21.44307,0,0,0,21.44307-21.44307v0A21.44307,21.44307,0,0,0,394.16787,579.148Z"
transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
.cls-1 { <path d="M1060.74162,579.148h-35.53428V474.50864a20.95586,20.95586,0,0,0-20.95586-20.95586H995.9251a20.95586,20.95586,0,0,0-20.95586,20.95586V579.148H874.73916a11.96327,11.96327,0,0,1-10.57763-17.552L970.24392,360.81572a20.95586,20.95586,0,0,0-9.58037-28.73893l-6.26231-2.9572a20.95586,20.95586,0,0,0-27.42931,9.07L797.59047,580.2222a28.41582,28.41582,0,0,0-3.35584,13.39613v0a28.41583,28.41583,0,0,0,28.41583,28.41583H974.96924v66.16727a25.119,25.119,0,0,0,25.119,25.119h0a25.119,25.119,0,0,0,25.119-25.119V622.0342h35.53428a21.44307,21.44307,0,0,0,21.44307-21.44307v0A21.44307,21.44307,0,0,0,1060.74162,579.148Z"
opacity: 0.3; transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
} <path d="M603.0848,313.86637c-85.94407,0-147.651,55.13937-147.651,183.79791,0,145.813,61.70691,184.41057,147.651,184.41057s151.327-42.27352,151.327-184.41057C754.41175,345.72467,689.02887,313.86637,603.0848,313.86637Zm.565,319.80836c-59.52686,0-90.62592-34.92287-90.62592-135.91629,0-89.11185,32.37209-136.10461,91.899-136.10461s91.899,30.86774,91.899,136.10461C696.82174,596.20579,663.17661,633.67473,603.64975,633.67473Z"
transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
.cls-7 { <circle cx="471.14108" cy="18.25044" r="12.90118" fill="#2f2e41"/>
opacity: 0.8; <ellipse cx="502.60736" cy="46.88476" rx="36.18622" ry="46.88476" fill="#2f2e41"/>
} <path d="M565.66136,237.49419c-18.1276,0-33.1413-17.27052-35.77576-39.80484a60.9759,60.9759,0,0,0-.41046,7.07991c0,25.89373,16.20114,46.88476,36.18622,46.88476s36.18623-20.991,36.18623-46.88476a60.9759,60.9759,0,0,0-.41046-7.07991C598.80267,220.22367,583.789,237.49419,565.66136,237.49419Z"
transform="translate(-63.054 -157.8845)" opacity="0.1"/>
.cls-2 { <path d="M639.29619,342.07326c-.77711,3.19345-4.12792,5.751-7.83881,7.53791-7.80188,3.75682-17.4253,4.87788-26.7597,5.25418a45.17622,45.17622,0,0,1-7.1445-.132,20.5371,20.5371,0,0,1-12.25052-5.63141,1.68086,1.68086,0,0,1,.04371-2.84388c4.9694-5.45888,13.2622-8.80605,21.61613-11.21609,6.3344-1.82743,17.3813-6.56089,24.29013-5.9221C637.94444,329.73864,640.2774,338.04112,639.29619,342.07326Z"
fill: #fff; transform="translate(-63.054 -157.8845)" fill="#3f3d56"/>
} <path d="M639.29619,342.07326c-.77711,3.19345-4.12792,5.751-7.83881,7.53791-7.80188,3.75682-17.4253,4.87788-26.7597,5.25418a45.17622,45.17622,0,0,1-7.1445-.132,20.5371,20.5371,0,0,1-12.25052-5.63141,1.68086,1.68086,0,0,1,.04371-2.84388c4.9694-5.45888,13.2622-8.80605,21.61613-11.21609,6.3344-1.82743,17.3813-6.56089,24.29013-5.9221C637.94444,329.73864,640.2774,338.04112,639.29619,342.07326Z"
transform="translate(-63.054 -157.8845)" opacity="0.1"/>
.cls-10, <path d="M540.09786,318.2059a19.76967,19.76967,0,0,0-1.1987,15.07476,26.33914,26.33914,0,0,0,8.82921,12.49683c10.09467,8.09163,23.98784,9.20512,36.92477,9.09278a284.6495,284.6495,0,0,0,33.90525-2.32384,40.53788,40.53788,0,0,0,11.00143-2.55442c4.22242-1.82679,7.93282-5.17756,9.436-9.5257s.43625-9.67246-3.13383-12.57428c-3.13686-2.54969-7.46265-2.9004-11.49775-3.14289l-23.08764-1.38745c2.281-2.30839,5.31816-3.614,8.09586-5.29216,3.68523-2.22642,6.13358-5.96455,8.81312-9.33471a129.00143,129.00143,0,0,1,13.4386-13.817c.75138,4.31038,3.4782,7.8499,6.68733,10.824s6.90841,5.36845,10.2439,8.20013c8.0786,6.85838,13.89583,16.1669,22.39215,22.50043a43.82885,43.82885,0,0,0,16.04862-8.0122l-3.30209-5.98141a3.94,3.94,0,0,0-1.24459-1.55282c-.93465-.575-2.13975-.27872-3.225-.44144-2.90082-.435-4.16771-3.784-5.306-6.48737-3.12491-7.42173-9.108-13.17993-14.21783-19.40381a98.00854,98.00854,0,0,1-9.99577-14.72284c-1.71652-3.10162-3.288-6.33107-5.61746-9.00321s-5.59358-4.773-9.1385-4.78051c-3.13222-.00662-6.02122,1.58355-8.71422,3.18308a230.47679,230.47679,0,0,0-23.63018,16.09894c-3.94376,3.0617-7.86306,6.29645-12.48933,8.17393-1.94748.79035-4.00044,1.33052-5.86924,2.29223-3.27313,1.6844-5.75721,4.53435-8.43128,7.06415C566.27712,311.89225,553.219,317.73841,540.09786,318.2059Z"
.cls-11, transform="translate(-63.054 -157.8845)" fill="#3f3d56"/>
.cls-12, <path d="M588.3737,253.98251a23.77444,23.77444,0,0,1-1.73379,8.03335,10.04492,10.04492,0,0,1-5.76772,5.57269,12.37513,12.37513,0,0,1-5.62306.18249,10.88232,10.88232,0,0,1-4.58151-1.56071c-2.16484-1.48837-3.24415-4.14413-3.63748-6.74325-.39333-2.596-.21714-5.24857-.46885-7.86342a42.94439,42.94439,0,0,0-1.202-6.25549c-.16993-.68282-.343-1.36248-.51294-2.04216-.16674-.67967-.33037-1.35935-.48141-2.039-.13847-.63878-.26745-1.28068-.37761-1.92574-.09123-.54436-.173-1.09189-.23285-1.64255a18.42329,18.42329,0,0,0-.80867-4.81118,14.60727,14.60727,0,0,0-1.68659-2.854c-.28635-.40906-.56326-.81811-.81815-1.24292a5.88984,5.88984,0,0,1-.97226-3.74763,3.286,3.286,0,0,1,.14788-.601c.02516-.07552.05347-.151.085-.2234A1.80187,1.80187,0,0,0,560.932,223.07a3.43341,3.43341,0,0,0-.14788-1.77783,11.31808,11.31808,0,0,0-.95974-2.28761c-.2643-.47829-1.16108-1.34046-1.16738-1.888-.0126-1.10132,2.13972-1.98867,3.01134-2.42291a16.79623,16.79623,0,0,1,8.59657-1.74323c1.90369.129,3.9679.71428,5.0189,2.30962.944,1.438.81807,3.30081,1.22085,4.97169a1.47068,1.47068,0,0,0,.29892.66393,1.34135,1.34135,0,0,0,.73948.33982,4.54948,4.54948,0,0,0,1.416.05666h.00315a2.93138,2.93138,0,0,0,.37128-.05351,4.957,4.957,0,0,0,2.03271-.8779q.58531-.15576,1.18-.25488a.25112.25112,0,0,0,.04725-.00945c1.57646,4.97482,1.781,10.30836,3.07111,15.37444.63874,2.52044,1.55442,5.00943,1.6834,7.60225.00945.11327.0126.2297.01575.34612.0189.83386-.04717,1.674-.0126,2.50472a6.981,6.981,0,0,0,.12591,1.1139,15.61121,15.61121,0,0,0,.52546,1.74325l.00945.02831c.05977.18251.11643.36817.16363.55381.03457.1353.06607.26747.09127.40277l.00311.00943A14.93754,14.93754,0,0,1,588.3737,253.98251Z"
.cls-14, transform="translate(-63.054 -157.8845)" fill="#fbbebe"/>
.cls-16, <circle cx="503.23669" cy="44.99678" r="18.56511" fill="#fbbebe"/>
.cls-3 { <path d="M684.15711,304.03278a30.445,30.445,0,0,0-5.236-14.10317q.72216,4.29513,1.44748,8.58714a3.214,3.214,0,0,1-3.36688-1.03523,10.33663,10.33663,0,0,1-1.76529-3.27565,67.46571,67.46571,0,0,0-8.2095-14.73567c-11.81876-.98489-23.50223-5.88418-33.89555-11.59532-10.39643-5.708-20.12582-12.5519-30.38382-18.50217a43.57346,43.57346,0,0,0-5.54436-2.832c-3.20954-1.287-6.81242-1.95406-9.85526-3.46759-.2045-.1007-.409-.20767-.61043-.31781a12.57834,12.57834,0,0,1-1.94459-1.30584,10.34363,10.34363,0,0,1-.93139-.8559,20.35115,20.35115,0,0,1-3.55886-5.95341c-1.63308-3.61232-2.21524-7.97041-3.84517-11.58274a11.20292,11.20292,0,0,1,2.50156-1.76525h.00315c.13213-.06924.2643-.13532.39962-.19824a11.9404,11.9404,0,0,1,2.00437-.73317q.58531-.15576,1.18-.25488a.25112.25112,0,0,0,.04725-.00945,11.56564,11.56564,0,0,1,5.49085.43424c2.58652.87477,4.76711,2.62115,6.94148,4.27313a114.02006,114.02006,0,0,1,10.14787,8.04908c1.79357,1.718,3.4298,3.606,5.35868,5.16676a42.14393,42.14393,0,0,0,5.05662,3.35116q15.65613,9.32658,31.31525,18.65005c3.53365,2.1051,7.07046,4.21019,10.52553,6.438,5.24855,3.38578,10.30828,7.05474,15.36493,10.72057q4.46978,3.23787,8.93647,6.47889a9.72771,9.72771,0,0,1,2.533,2.3411,8.4724,8.4724,0,0,1,1.12337,3.433A31.3874,31.3874,0,0,1,684.15711,304.03278Z"
fill: none; transform="translate(-63.054 -157.8845)" fill="#fbbebe"/>
} <path d="M592.97726,267.9441c-1.25235,5.61674-6.92888,9.012-9.89617,13.94586-3.68784,6.12335-2.18378,13.241-.79922,20.25484q-3.79485,3.27095-7.59285,6.54186c-1.39708,1.19886-2.79417,2.404-4.29827,3.46444a57.35064,57.35064,0,0,1-6.85966,3.93956q-3.3606,1.72752-6.72119,3.45814a32.1282,32.1282,0,0,1-6.57961,2.78793c-4.41473,1.13278-9.10318.33982-13.4707-.97232a6.08761,6.08761,0,0,1-1.47264-.601,2.39351,2.39351,0,0,1-.69854-.63248,3.91067,3.91067,0,0,1-.44365-2.53933c.44365-7.35052,2.24036-14.54686,4.03081-21.68971a85.2598,85.2598,0,0,1,3.84832-12.57708,85.0766,85.0766,0,0,1,5.41538-10.151,68.36751,68.36751,0,0,1,7.92948-11.51353,18.47881,18.47881,0,0,0,3.67525-4.73882c1.11706-2.54876.686-5.472.91252-8.24732a17.14844,17.14844,0,0,1,1.63312-6.0069v-.00315a17.09326,17.09326,0,0,1,1.74321-2.88232q.45788,1.06671.91568,2.13027.30209.69855.59783,1.394.38706.89679.7678,1.78728,1.09973,2.55823,2.19637,5.11327a21.58968,21.58968,0,0,0,3.33538,5.944,6.49923,6.49923,0,0,0,11.12337-.85275,21.26125,21.26125,0,0,0,2.27185-6.0132,19.21547,19.21547,0,0,0,.25175-7.83509c-.75835-5.00945-2.88862-10.12585-4.43678-14.77972a14.94511,14.94511,0,0,1-1.07927-4.871,3.35144,3.35144,0,0,1,.05662-.56011c.00945-.04719.0189-.09754.02834-.14473a11.9404,11.9404,0,0,1,2.00437-.73317q.58531-.15576,1.18-.25488,2.04378,11.06355,4.09377,22.12709c.0315.17307.0661.34613.09756.52234.19509,1.05726.39333,2.11454.61358,3.16865.19828.95657.41223,1.91.65137,2.85715l.00945.02831c.08182.321.16678.63877.2549.95658l.00311.00943c.2423.86848.5129,1.73065.81811,2.58024C590.93825,257.47528,594.16355,262.62946,592.97726,267.9441Z"
transform="translate(-63.054 -157.8845)" fill="#16a085"/>
.cls-3 { <path d="M668.32144,346.87707a6.58269,6.58269,0,0,0,.61,3.14328c1.16192,2.12353,3.94981,2.60625,6.36228,2.80484a188.37688,188.37688,0,0,0,42.2657-1.28774,4.88565,4.88565,0,0,0,2.15136-.66766c1.98985-1.39509.76329-4.7951-1.40951-5.88355s-4.75126-.82614-7.1353-1.29748a22.47912,22.47912,0,0,1-6.67794-2.89617q-7.25234-4.16669-14.293-8.68808c-2.79453-1.79464-6.09272-3.70993-9.23987-2.64587C672.43,332.34264,668.26533,337.68065,668.32144,346.87707Z"
stroke: #5c7690; transform="translate(-63.054 -157.8845)" fill="#3f3d56"/>
} <path d="M564.43732,240.87367v.00315c-.022.13215-.04406.26116-.07237.39018-.0346.214-.07551.43108-.11642.645-.39018,1.99812-.86847,3.98678-1.41913,5.96287-1.5104,5.45939-3.53366,10.83069-5.54121,16.12332q-8.08055,21.28692-16.16423,42.577c-1.35936,3.57457-2.71554,7.15228-4.26054,10.65448-.516,1.16741-1.04782,2.34424-1.57647,3.53368-1.89427,4.25737-3.713,8.65322-4.31716,13.18436a27.44976,27.44976,0,0,0-.19194,9.04027c.60416,2.97042,2.40718,5.8716,5.22969,6.96977,1.37823.53808,3.35113,1.25865,2.97355,2.69037-.2045.78665-1.09817,1.17055-1.90057,1.3027a7.31234,7.31234,0,0,1-5.966-1.718c-1.50725-1.33732-2.66518-3.41725-4.66959-3.64065-1.38767-.151-2.66518.67966-3.93643,1.26178-5.18564,2.36942-11.22719.71114-16.674-.9723.42794-2.20579,2.64318-3.65953,4.84267-4.10006,2.19949-.44367,4.47449-.129,6.718-.18879a3.50958,3.50958,0,0,0,2.04216-.52549,3.70545,3.70545,0,0,0,1.10132-1.88169,78.96356,78.96356,0,0,0,3.21273-13.14661c.7237-4.66645,1.02581-9.40527,2.05787-14.01507.80241-3.59661,2.0422-7.07991,3.10572-10.61044a224.68238,224.68238,0,0,0,5.0598-22.07674,78.02019,78.02019,0,0,0,1.42543-9.36751c.17935-2.6117.09438-5.236.34609-7.83826a60.8877,60.8877,0,0,1,2.11141-9.99683q1.44427-5.34769,2.88547-10.68911c1.42544-5.2706,2.95465-10.74572,6.567-14.84264a13.96159,13.96159,0,0,1,10.02834-4.78915,9.8819,9.8819,0,0,1,2.13027.22969c.11639.02831.23285.05664.34923.0881a8.63447,8.63447,0,0,1,2.17437.89995c1.11388-.708,1.68025-.45942,2.41974.63246a6.97319,6.97319,0,0,1,.88107,3.79485A52.42378,52.42378,0,0,1,564.43732,240.87367Z"
transform="translate(-63.054 -157.8845)" fill="#fbbebe"/>
.cls-10, <path d="M565.66136,245.0461l-.0472.04719-.25486.25488-2.5299,2.52675-1.23976-5.20767-4.25109-17.854a9.8819,9.8819,0,0,1,2.13027.22969,3.286,3.286,0,0,1,.14788-.601l.20135.68911,1.44118,4.90245,2.72811,9.30773.45,1.53241v.00315Z"
.cls-11, transform="translate(-63.054 -157.8845)" fill="#16a085"/>
.cls-12, <path d="M581.71523,188.0873a12.58165,12.58165,0,0,1-3.70049,8.89583,12.31392,12.31392,0,0,1-1.36008,1.17634,12.52812,12.52812,0,0,1-7.53567,2.52415H554.023a12.5902,12.5902,0,0,1,0-25.18037h15.096A12.62919,12.62919,0,0,1,581.71523,188.0873Z"
.cls-3 { transform="translate(-63.054 -157.8845)" fill="#2f2e41"/>
stroke-miterlimit: 10; <circle cx="532.81499" cy="18.25044" r="12.90118" fill="#2f2e41"/>
} <path d="M595.55433,163.23377c-.15825,0-.31505.00628-.472.01193a12.89776,12.89776,0,0,1,0,25.77849c.15694.00565.31374.01193.472.01193a12.90117,12.90117,0,1,0,0-25.80235Z"
transform="translate(-63.054 -157.8845)" opacity="0.1"/>
.cls-14, <path d="M534.19508,163.23377c.15825,0,.31505.00628.472.01193a12.89776,12.89776,0,0,0,0,25.77849c-.157.00565-.31375.01193-.472.01193a12.90118,12.90118,0,0,1,0-25.80235Z"
.cls-15, transform="translate(-63.054 -157.8845)" opacity="0.1"/>
.cls-16, <path d="M576.65466,198.15947a12.52812,12.52812,0,0,1-7.53567,2.52415H554.023a12.52833,12.52833,0,0,1-7.53574-2.52415Z"
.cls-3 { transform="translate(-63.054 -157.8845)" opacity="0.1"/>
stroke-width: 0.5px; <path d="M674.13958,291.64042s3.25228,9.37161,6.229,6.87633L677.996,286.26693Z"
} transform="translate(-63.054 -157.8845)" fill="#fbbebe"/>
<path d="M1069.91781,577.43414a20.81252,20.81252,0,1,0,2.7716-39.91524l.52093,10.7122-5.06814-9.18045a20.734,20.734,0,0,0-10.68367,11.72261,20.40847,20.40847,0,0,0-1.19713,5.62986A20.80856,20.80856,0,0,0,1069.91781,577.43414Z"
.cls-4 { transform="translate(-63.054 -157.8845)" fill="#57b894"/>
fill: #ffe1d9; <path d="M1094.99516,701.67756c-1.78906-9.11027,5.9633-17.1868,13.62086-22.43651s16.605-10.40779,19.21775-19.31684c3.755-12.80387-7.43-24.52981-16.13564-34.64176a125.30044,125.30044,0,0,1-16.52359-24.55738c-1.81107-3.5325-3.47558-7.22528-3.95221-11.16626-.68641-5.67546,1.13693-11.32309,2.9739-16.73673q9.17925-27.05169,19.62843-53.65005"
} transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
<path d="M1070.77493,574.6762a20.81252,20.81252,0,1,0,2.7716-39.91524l.52093,10.7122-5.06815-9.18045a20.734,20.734,0,0,0-10.68366,11.72261,20.40847,20.40847,0,0,0-1.19713,5.62986A20.80855,20.80855,0,0,0,1070.77493,574.6762Z"
.cls-5 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
fill: #ffcfbf; <path d="M1092.45136,515.47266a20.78819,20.78819,0,0,1,14.97993-13.19764l1.71361,10.18378,3.177-10.69566a20.81,20.81,0,1,1-19.87057,13.70952Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M1093.59418,511.7954a20.7882,20.7882,0,0,1,14.97993-13.19763l1.71361,10.18378,3.177-10.69567a20.81,20.81,0,1,1-19.87057,13.70952Z"
.cls-6 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
fill: #fecbb6; <path d="M1108.04474,625.48885a20.81,20.81,0,0,0,18.419-37.02267l-2.44121,8.21926-1.73105-10.30382a.36183.36183,0,0,0-.053-.0201,20.81113,20.81113,0,1,0-14.1938,39.12733Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M1109.035,621.76417a20.81,20.81,0,0,0,18.419-37.02267l-2.44121,8.21926-1.73105-10.30382a.3621.3621,0,0,0-.053-.0201,20.81113,20.81113,0,1,0-14.1938,39.12733Z"
.cls-9 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
fill: #fecb02; <path d="M1086.37782,660.05148a20.80131,20.80131,0,1,0,4.01058-16.29737l9.27267,13.95654-12.66994-7.40768A20.61638,20.61638,0,0,0,1086.37782,660.05148Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M1087.23494,657.29354a20.80131,20.80131,0,1,0,4.01058-16.29737l9.27267,13.95655-12.66994-7.40769A20.61626,20.61626,0,0,0,1087.23494,657.29354Z"
.cls-10, transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
.cls-12 { <path d="M72.06146,628.13325a13.67421,13.67421,0,1,0,1.821-26.225l.34227,7.03811-3.32987-6.03172a13.62263,13.62263,0,0,0-7.01936,7.702,13.40883,13.40883,0,0,0-.78654,3.69893A13.6716,13.6716,0,0,0,72.06146,628.13325Z"
stroke: #d26f51; transform="translate(-63.054 -157.8845)" fill="#57b894"/>
} <path d="M88.53774,709.76344c-1.17545-5.98561,3.918-11.292,8.94915-14.7412s10.90978-6.8381,12.62642-12.69151c2.46711-8.41238-4.88167-16.11653-10.60142-22.76027A82.32442,82.32442,0,0,1,88.6556,643.43581a22.20962,22.20962,0,0,1-2.59668-7.33643c-.451-3.72888.747-7.43947,1.95391-10.99634q6.03093-17.77346,12.89623-35.24906"
transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
.cls-10, <path d="M72.62461,626.32123a13.6742,13.6742,0,1,0,1.821-26.225l.34227,7.03812L71.458,601.10258a13.62262,13.62262,0,0,0-7.01936,7.702,13.40912,13.40912,0,0,0-.78654,3.69892A13.67158,13.67158,0,0,0,72.62461,626.32123Z"
.cls-11 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
stroke-width: 0.38px; <path d="M86.86641,587.42343a13.65822,13.65822,0,0,1,9.84209-8.67109l1.12587,6.69093,2.08737-7.02725a13.67252,13.67252,0,1,1-13.05533,9.00741Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M87.61727,585.0074a13.65822,13.65822,0,0,1,9.84209-8.67108l1.12587,6.69093L100.6726,576a13.67252,13.67252,0,1,1-13.05533,9.0074Z"
.cls-11 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
stroke: #000; <path d="M97.11155,659.70607a13.67255,13.67255,0,0,0,12.10164-24.32457l-1.60392,5.4002-1.13733-6.76979a.238.238,0,0,0-.0348-.0132,13.67329,13.67329,0,1,0-9.32559,25.70736Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M97.76214,657.25889a13.67255,13.67255,0,0,0,12.10164-24.32457l-1.60392,5.4002-1.13733-6.7698a.238.238,0,0,0-.0348-.0132,13.67329,13.67329,0,1,0-9.32559,25.70737Z"
.cls-12 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
stroke-width: 0.19px; <path d="M82.876,682.41435a13.66684,13.66684,0,1,0,2.635-10.70767l6.09231,9.16971-8.32438-4.867A13.54535,13.54535,0,0,0,82.876,682.41435Z"
} transform="translate(-63.054 -157.8845)" fill="#57b894"/>
<path d="M83.43913,680.60233a13.66684,13.66684,0,1,0,2.635-10.70767l6.09231,9.16971-8.32439-4.867A13.54535,13.54535,0,0,0,83.43913,680.60233Z"
.cls-13 { transform="translate(-63.054 -157.8845)" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
opacity: 0.45; <ellipse cx="480.946" cy="319.1155" rx="17" ry="22" fill="#2f2e41"/>
} <ellipse cx="573.446" cy="319.6155" rx="17" ry="22" fill="#2f2e41"/>
<path d="M623.5,542.5c0,9.94-13.88,18-31,18s-31-8.06-31-18c0-8.61,10.41-15.81,24.32-17.57a50.10353,50.10353,0,0,1,6.68-.43,50.69869,50.69869,0,0,1,11.13,1.2C615.25,528.29,623.5,534.84,623.5,542.5Z"
.cls-14, transform="translate(-63.054 -157.8845)" fill="#2f2e41"/>
.cls-15, <ellipse cx="484.946" cy="314.1155" rx="17" ry="22" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
.cls-16 { <ellipse cx="577.446" cy="314.6155" rx="17" ry="22" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
stroke: #b0bec5; <ellipse cx="533.446" cy="379.6155" rx="31" ry="18" fill="none" stroke="#3f3d56" stroke-miterlimit="10"/>
stroke-linejoin: round; <path d="M604,527.2a4.93658,4.93658,0,0,1-1.32,3.392A4.33873,4.33873,0,0,1,599.5,532h-10a4.66433,4.66433,0,0,1-4.5-4.8,4.90458,4.90458,0,0,1,.82-2.74134A47.02,47.02,0,0,1,592.5,524a47.66454,47.66454,0,0,1,11.13,1.28A5.06656,5.06656,0,0,1,604,527.2Z"
} transform="translate(-63.054 -157.8845)" fill="#fff"/>
<circle cx="484.946" cy="308.1155" r="5" fill="#fff"/>
.cls-15 { <circle cx="577.946" cy="308.1155" r="5" fill="#fff"/>
fill: #edf0f2; <circle cx="582.946" cy="355.1155" r="5" fill="#16a085" opacity="0.3"/>
} <circle cx="460.946" cy="355.1155" r="5" fill="#16a085" opacity="0.3"/>
.cls-16 {
stroke-linecap: round;
}
.cls-17 {
font-family: 'PT Sans', sans-serif;
font-size: 49.87px;
font-weight: 700;
}
.cls-18 {
fill: #fffdbb;
opacity: 0.5;
}
/*---------------------------
SVG Animate
---------------------------*/
.earMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: earmove 1.5s linear infinite alternate;
}
.faceMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: move 1.5s linear infinite alternate;
}
.neckMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: neck 1.5s linear infinite alternate;
}
@keyframes earmove {
0% {
transform: translateX(-0.3px) translateY(0.6px);
}
30% {
transform: translateX(-0.3px) translateY(0.6px);
}
60% {
transform: translateX(-0.7px) translateY(0px);
}
70% {
transform: translateX(-0.7px) translateY(-0.3px);
}
100% {
transform: translateX(-0.7px) translateY(-0.3px);
}
}
@keyframes move {
0% {
transform: translateX(-0.3px) translateY(0.6px);
}
30% {
transform: translateX(-0.3px) translateY(0.6px);
}
60% {
transform: translateX(2px) translateY(0px);
}
70% {
transform: translateX(2px) translateY(-0.3px);
}
100% {
transform: translateX(2px) translateY(-0.3px);
}
}
@keyframes neck {
0% {
transform: translateY(0.7px);
}
50% {
transform: translateY(0.7px);
}
100% {
transform: translateY(0px);
}
}
</style>
<path id="c-1" class="changeColor cls-1"
d="M46.62,52.5c5.78,4.9,21.14,8.4,39.19,8.4s33.41-3.5,39.19-8.4c-5.78-4.9-21.14-8.4-39.19-8.4S52.41,47.6,46.62,52.5Z"
style="fill: rgb(0, 21, 255);"></path>
<path class="cls-2"
d="M99.73,47.71H68.65a7.13,7.13,0,0,0-7.13,7.13V60a152.58,152.58,0,0,0,24.3,1.83,157.87,157.87,0,0,0,21.05-1.35V54.84A7.13,7.13,0,0,0,99.73,47.71Z"></path>
<path class="cls-3"
d="M123.56,55.81C115,58.94,101.27,61,85.81,61c-26,0-47-5.71-47-12.76,0-3.45,5.05-6.58,13.25-8.88"></path>
<path class="cls-3" d="M55.37,38.47a140,140,0,0,1,30.44-3c26,0,47,5.71,47,12.76,0,2.4-2.44,4.65-6.69,6.57"></path>
<path class="cls-3" d="M53.41,38.95l.94-.24"></path>
<path class="cls-4"
d="M91.68,47.71l-.75-11.2L79.15,43.84l-1.69,3.87H75.79c0,3.36,3.76,6.08,8.4,6.08s8.4-2.72,8.4-6.08Z"></path>
<path class="cls-5 neckMove"
d="M78,46.53a27.19,27.19,0,0,0,6.41.82c3.1,0,7.11-2.19,7.11-2.19l-.42-6.2L79.15,43.84Z"></path>
<polygon class="earMove" points="92.59 32.22 92.59 28.5 76.77 27.71 76.77 32.22 92.59 32.22"></polygon>
<circle class="cls-6 earMove" cx="78.06" cy="34.04" r="2.47"></circle>
<path class="cls-4"
d="M81.74,57.06,60.63,49.72h0A6.72,6.72,0,1,0,57.7,62.49H93.25C93.25,56.78,81.74,57.06,81.74,57.06Z"></path>
<path class="cls-4"
d="M77.46,25H90.92a0,0,0,0,1,0,0V39.38a6.73,6.73,0,0,1-6.73,6.73h0a6.73,6.73,0,0,1-6.73-6.73V25A0,0,0,0,1,77.46,25Z"></path>
<rect id="c-2" class="changeColor cls-7" x="74.82" y="26.48" width="19.14" height="2.45"
transform="translate(1.29 -3.65) rotate(2.49)" style="fill: rgb(0, 21, 255);"></rect>
<path id="c-3" class="changeColor cls-7"
d="M84.36,18.69h.5a7.8,7.8,0,0,1,7.8,7.8v0a0,0,0,0,1,0,0H76.56a0,0,0,0,1,0,0v0A7.8,7.8,0,0,1,84.36,18.69Z"
transform="translate(1.06 -3.66) rotate(2.49)" style="fill: rgb(0, 21, 255);"></path>
<polygon id="c-4" class="changeColor cls-8"
points="82.44 23.89 92.18 24.32 92.59 24.34 92.48 26.84 80.96 26.33 82.44 23.89"
style="fill: rgb(0, 21, 255);"></polygon>
<circle class="cls-9 faceMove" cx="78.72" cy="23.73" r="3.73"
transform="translate(51.58 101.34) rotate(-87.51)"></circle>
<circle class="cls-2 faceMove" cx="78.72" cy="23.73" r="2.36"
transform="translate(51.58 101.34) rotate(-87.51)"></circle>
<circle class="cls-4 earMove" cx="90.92" cy="34.04" r="2.47"></circle>
<path class="cls-4"
d="M112.2,53l-9.87-21.92-3-5.48-11.86-.22,7.42,3.35H91.55l5.82,4.58,2,22.26h0A6.72,6.72,0,1,0,112.2,53Z"></path>
<ellipse class="faceMove" cx="80.09" cy="33.12" rx="0.53" ry="0.59"></ellipse>
<ellipse class="faceMove" cx="86.34" cy="33.12" rx="0.53" ry="0.59"></ellipse>
<polyline class="cls-10 faceMove" points="84.19 31.08 81.74 37.01 84.39 37.01"></polyline>
<path class="cls-10 faceMove" d="M83.06,40.36a4,4,0,0,1,2.75-1"></path>
<line class="cls-11 faceMove" x1="81.07" y1="30.33" x2="78.47" y2="30.58"></line>
<line class="cls-11 faceMove" x1="86.34" y1="30.45" x2="88.15" y2="31.08"></line>
<line class="cls-12" x1="106.86" y1="47.82" x2="110.99" y2="46.11"></line>
<line class="cls-12" x1="107.43" y1="49.9" x2="111.55" y2="48.19"></line>
<line class="cls-12" x1="107.99" y1="51.98" x2="112.11" y2="50.27"></line>
<g class="cls-13">
<rect class="cls-14" x="85.81" y="2.46" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="96.58" y="2.46" width="10.77" height="3.5"></rect>
<rect class="cls-14" x="92.19" y="5.95" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="107.36" y1="5.95" x2="109.63" y2="5.95"></line>
<line class="cls-16" x1="110.68" y1="5.95" x2="111.57" y2="5.95"></line>
</g>
<g class="cls-13">
<rect class="cls-16" x="125" y="23.12" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="130.39" y="26.62" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="119.62" y="26.62" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="141.16" y1="26.62" x2="145.73" y2="26.62"></line>
<line class="cls-16" x1="125" y1="23.12" x2="115.4" y2="23.12"></line>
<line class="cls-16" x1="117.95" y1="26.62" x2="115.4" y2="26.62"></line>
</g>
<g class="cls-13">
<rect class="cls-16" x="39.34" y="16.12" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="39.34" y="23.11" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="50.11" y="23.11" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="50.11" y="16.12" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="44" y="19.61" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="33.23" y="19.61" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="60.89" y1="19.61" x2="65.51" y2="19.61"></line>
<line class="cls-16" x1="39.34" y1="16.12" x2="35.46" y2="16.12"></line>
<line class="cls-16" x1="36.45" y1="26.61" x2="33.23" y2="26.61"></line>
<line class="cls-16" x1="63.2" y1="23.11" x2="65.51" y2="23.11"></line>
</g>
<polyline class="cls-3" points="115.4 58.12 115.4 38.27 120.2 37.01"></polyline>
<polyline class="cls-3" points="129.01 53.21 129.01 43.14 131.74 42.13"></polyline>
<path class="cls-3" d="M115.4,42.13a53.27,53.27,0,0,1,8,2A42,42,0,0,1,129,47"></path>
<path class="cls-3" d="M115.4,47.34a53.27,53.27,0,0,1,8,2A42,42,0,0,1,129,52.22"></path>
<path class="cls-3" d="M115.4,52.56a53.27,53.27,0,0,1,8,2l1,.42"></path>
<path class="cls-18 faceMove"
d="M78.84,26.09l0-4.71L68.05,18.32a.91.91,0,0,0-.45-.13c-1.17,0-2.11,2.46-2.11,5.5s.95,5.5,2.11,5.5a.9.9,0,0,0,.44-.12Z"></path>
<path class="cls-5" d="M57.7,62.49H93.25A3.67,3.67,0,0,0,92.92,61H53.43A6.69,6.69,0,0,0,57.7,62.49Z"></path>
<path class="cls-12" d="M88.15,60.27s1.7.95,1.7,2.22"></path>
<path class="cls-5" d="M101.81,61a6.68,6.68,0,0,0,8.51,0Z"></path>
<polygon class="cls-5" points="90.92 30.25 77.46 29.69 77.46 28.64 90.92 29.22 90.92 30.25"></polygon>
<text id="title" transform="matrix(1 0 0 1 44.7249 78)">Oops, Page not found</text>
</svg> </svg>
<!-- <h1 id="title">404 Page not found</h1> -->
<!-- <button id="newBtn" class="btn" disabled>&#8594; &nbsp;Back to Home</button> -->

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,277 +1,75 @@
<svg id="mainImage_create" data-name="mainImage" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 171.2 81.5"> <svg id="aa03ddf9-f8f2-4819-a4ce-be9b0a220741" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1119.60911"
<style id="svgStyle"> height="699" viewBox="0 0 1119.60911 699">
@import url('https://fonts.googleapis.com/css?family=Merriweather'); <title>server down</title>
.changeColor { <circle cx="292.60911" cy="213" r="213" fill="#f2f2f2"/>
fill: #4f86ed; <path d="M31.39089,151.64237c0,77.49789,48.6181,140.20819,108.70073,140.20819"
} transform="translate(-31.39089 -100.5)" fill="#2f2e41"/>
<path d="M140.09162,291.85056c0-78.36865,54.255-141.78356,121.30372-141.78356"
#title { transform="translate(-31.39089 -100.5)" fill="#16a085"/>
font-size: 50%; <path d="M70.77521,158.66768c0,73.61476,31.00285,133.18288,69.31641,133.18288"
font-family: 'Merriweather', serif; transform="translate(-31.39089 -100.5)" fill="#16a085"/>
} <path d="M140.09162,291.85056c0-100.13772,62.7103-181.16788,140.20819-181.16788"
transform="translate(-31.39089 -100.5)" fill="#2f2e41"/>
.cls-1 { <path d="M117.22379,292.83905s15.41555-.47479,20.06141-3.783,23.713-7.2585,24.86553-1.95278,23.16671,26.38821,5.76263,26.5286-40.43935-2.711-45.07627-5.53549S117.22379,292.83905,117.22379,292.83905Z"
opacity: 0.3; transform="translate(-31.39089 -100.5)" fill="#a8a8a8"/>
} <path d="M168.224,311.78489c-17.40408.14042-40.43933-2.71094-45.07626-5.53548-3.53126-2.151-4.93843-9.86945-5.40926-13.43043-.32607.014-.51463.02-.51463.02s.97638,12.43276,5.61331,15.2573,27.67217,5.67589,45.07626,5.53547c5.02386-.04052,6.7592-1.82793,6.66391-4.47526C173.87935,310.756,171.96329,311.75474,168.224,311.78489Z"
transform="translate(-31.39089 -100.5)" opacity="0.2"/>
.cls-7 { <ellipse cx="198.60911" cy="424.5" rx="187" ry="25.43993" fill="#3f3d56"/>
opacity: 0.8; <ellipse cx="198.60911" cy="424.5" rx="157" ry="21.35866" opacity="0.1"/>
} <ellipse cx="836.60911" cy="660.5" rx="283" ry="38.5" fill="#3f3d56"/>
<ellipse cx="310.60911" cy="645.5" rx="170" ry="23.12721" fill="#3f3d56"/>
.cls-2 { <path d="M494,726.5c90,23,263-30,282-90" transform="translate(-31.39089 -100.5)" fill="none" stroke="#2f2e41"
fill: #fff; stroke-miterlimit="10" stroke-width="2"/>
} <path d="M341,359.5s130-36,138,80-107,149-17,172" transform="translate(-31.39089 -100.5)" fill="none"
stroke="#2f2e41" stroke-miterlimit="10" stroke-width="2"/>
.cls-10, <path d="M215.40233,637.78332s39.0723-10.82,41.47675,24.04449-32.15951,44.78287-5.10946,51.69566"
.cls-11, transform="translate(-31.39089 -100.5)" fill="none" stroke="#2f2e41" stroke-miterlimit="10" stroke-width="2"/>
.cls-12, <path d="M810.09554,663.73988,802.218,714.03505s-38.78182,20.60284-11.51335,21.20881,155.73324,0,155.73324,0,24.84461,0-14.54318-21.81478l-7.87756-52.719Z"
.cls-14, transform="translate(-31.39089 -100.5)" fill="#2f2e41"/>
.cls-16, <path d="M785.21906,734.69812c6.193-5.51039,16.9989-11.252,16.9989-11.252l7.87756-50.2952,113.9216.10717,7.87756,49.582c9.185,5.08711,14.8749,8.987,18.20362,11.97818,5.05882-1.15422,10.58716-5.44353-18.20362-21.38921l-7.87756-52.719-113.9216,3.02983L802.218,714.03506S769.62985,731.34968,785.21906,734.69812Z"
.cls-3 { transform="translate(-31.39089 -100.5)" opacity="0.1"/>
fill: none; <rect x="578.43291" y="212.68859" width="513.25314" height="357.51989" rx="18.04568" fill="#2f2e41"/>
} <rect x="595.70294" y="231.77652" width="478.71308" height="267.83694" fill="#3f3d56"/>
<circle cx="835.05948" cy="223.29299" r="3.02983" fill="#f2f2f2"/>
.cls-3 { <path d="M1123.07694,621.32226V652.6628a18.04341,18.04341,0,0,1-18.04568,18.04568H627.86949A18.04341,18.04341,0,0,1,609.8238,652.6628V621.32226Z"
stroke: #5c7690; transform="translate(-31.39089 -100.5)" fill="#2f2e41"/>
} <polygon
points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 651.452 645.651 962.312 645.651 968.978 667.466"
.cls-10, fill="#2f2e41"/>
.cls-11, <path d="M1125.828,762.03359c-.59383,2.539-2.83591,5.21743-7.90178,7.75032-18.179,9.08949-55.1429-2.42386-55.1429-2.42386s-28.4804-4.84773-28.4804-17.573a22.72457,22.72457,0,0,1,2.49658-1.48459c7.64294-4.04351,32.98449-14.02122,77.9177.42248a18.73921,18.73921,0,0,1,8.54106,5.59715C1125.07908,756.45353,1126.50669,759.15715,1125.828,762.03359Z"
.cls-12, transform="translate(-31.39089 -100.5)" fill="#2f2e41"/>
.cls-3 { <path d="M1125.828,762.03359c-22.251,8.526-42.0843,9.1622-62.43871-4.975-10.26507-7.12617-19.59089-8.88955-26.58979-8.75618,7.64294-4.04351,32.98449-14.02122,77.9177.42248a18.73921,18.73921,0,0,1,8.54106,5.59715C1125.07908,756.45353,1126.50669,759.15715,1125.828,762.03359Z"
stroke-miterlimit: 10; transform="translate(-31.39089 -100.5)" opacity="0.1"/>
} <ellipse cx="1066.53846" cy="654.13477" rx="7.87756" ry="2.42386" fill="#f2f2f2"/>
<circle cx="835.05948" cy="545.66686" r="11.51335" fill="#f2f2f2"/>
.cls-14, <polygon points="968.978 667.466 968.978 673.526 642.968 673.526 642.968 668.678 643.417 667.466 968.978 667.466"
.cls-15, opacity="0.1"/>
.cls-16, <rect x="108.60911" y="159" width="208" height="242" fill="#2f2e41"/>
.cls-3 { <rect x="87.60911" y="135" width="250" height="86" fill="#3f3d56"/>
stroke-width: 0.5px; <rect x="87.60911" y="237" width="250" height="86" fill="#3f3d56"/>
} <rect x="87.60911" y="339" width="250" height="86" fill="#3f3d56"/>
<rect x="271.60911" y="150" width="16" height="16" fill="#16a085" opacity="0.4"/>
.cls-4 { <rect x="294.60911" y="150" width="16" height="16" fill="#16a085" opacity="0.8"/>
fill: #ffe1d9; <rect x="317.60911" y="150" width="16" height="16" fill="#16a085"/>
} <rect x="271.60911" y="251" width="16" height="16" fill="#16a085" opacity="0.4"/>
<rect x="294.60911" y="251" width="16" height="16" fill="#16a085" opacity="0.8"/>
.cls-5 { <rect x="317.60911" y="251" width="16" height="16" fill="#16a085"/>
fill: #ffcfbf; <rect x="271.60911" y="352" width="16" height="16" fill="#16a085" opacity="0.4"/>
} <rect x="294.60911" y="352" width="16" height="16" fill="#16a085" opacity="0.8"/>
<rect x="317.60911" y="352" width="16" height="16" fill="#16a085"/>
.cls-6 { <circle cx="316.60911" cy="538" r="79" fill="#2f2e41"/>
fill: #fecbb6; <rect x="280.60911" y="600" width="24" height="43" fill="#2f2e41"/>
} <rect x="328.60911" y="600" width="24" height="43" fill="#2f2e41"/>
<ellipse cx="300.60911" cy="643.5" rx="20" ry="7.5" fill="#2f2e41"/>
.cls-9 { <ellipse cx="348.60911" cy="642.5" rx="20" ry="7.5" fill="#2f2e41"/>
fill: #fecb02; <circle cx="318.60911" cy="518" r="27" fill="#fff"/>
} <circle cx="318.60911" cy="518" r="9" fill="#3f3d56"/>
<path d="M271.36733,565.03228c-6.37889-28.56758,14.01185-57.43392,45.544-64.47477s62.2651,10.41,68.644,38.9776-14.51861,39.10379-46.05075,46.14464S277.74622,593.59986,271.36733,565.03228Z"
.cls-10, transform="translate(-31.39089 -100.5)" fill="#16a085"/>
.cls-12 { <ellipse cx="417.21511" cy="611.34365" rx="39.5" ry="12.40027"
stroke: #d26f51; transform="translate(-238.28665 112.98044) rotate(-23.17116)" fill="#2f2e41"/>
} <ellipse cx="269.21511" cy="664.34365" rx="39.5" ry="12.40027"
transform="translate(-271.07969 59.02084) rotate(-23.17116)" fill="#2f2e41"/>
.cls-10, <path d="M394,661.5c0,7.732-19.90861,23-42,23s-43-14.268-43-22,20.90861-6,43-6S394,653.768,394,661.5Z"
.cls-11 { transform="translate(-31.39089 -100.5)" fill="#fff"/>
stroke-width: 0.38px;
}
.cls-11 {
stroke: #000;
}
.cls-12 {
stroke-width: 0.19px;
}
.cls-13 {
opacity: 0.45;
}
.cls-14,
.cls-15,
.cls-16 {
stroke: #b0bec5;
stroke-linejoin: round;
}
.cls-15 {
fill: #edf0f2;
}
.cls-16 {
stroke-linecap: round;
}
.cls-17 {
font-family: 'PT Sans', sans-serif;
font-size: 49.87px;
font-weight: 700;
}
.cls-18 {
fill: #fffdbb;
opacity: 0.5;
}
/*---------------------------
SVG Animate
---------------------------*/
.earMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: earmove 1.5s linear infinite alternate;
}
.faceMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: move 1.5s linear infinite alternate;
}
.neckMove {
transition: all ease-in-out 2s;
transform-origin: 50% 50%;
animation: neck 1.5s linear infinite alternate;
}
@keyframes earmove {
0% {
transform: translateX(-0.3px) translateY(0.6px);
}
30% {
transform: translateX(-0.3px) translateY(0.6px);
}
60% {
transform: translateX(-0.7px) translateY(0px);
}
70% {
transform: translateX(-0.7px) translateY(-0.3px);
}
100% {
transform: translateX(-0.7px) translateY(-0.3px);
}
}
@keyframes move {
0% {
transform: translateX(-0.3px) translateY(0.6px);
}
30% {
transform: translateX(-0.3px) translateY(0.6px);
}
60% {
transform: translateX(2px) translateY(0px);
}
70% {
transform: translateX(2px) translateY(-0.3px);
}
100% {
transform: translateX(2px) translateY(-0.3px);
}
}
@keyframes neck {
0% {
transform: translateY(0.7px);
}
50% {
transform: translateY(0.7px);
}
100% {
transform: translateY(0px);
}
}
</style>
<path id="c-1" class="changeColor cls-1"
d="M46.62,52.5c5.78,4.9,21.14,8.4,39.19,8.4s33.41-3.5,39.19-8.4c-5.78-4.9-21.14-8.4-39.19-8.4S52.41,47.6,46.62,52.5Z"
style="fill: rgb(255, 5, 18);"></path>
<path class="cls-2"
d="M99.73,47.71H68.65a7.13,7.13,0,0,0-7.13,7.13V60a152.58,152.58,0,0,0,24.3,1.83,157.87,157.87,0,0,0,21.05-1.35V54.84A7.13,7.13,0,0,0,99.73,47.71Z"></path>
<path class="cls-3"
d="M123.56,55.81C115,58.94,101.27,61,85.81,61c-26,0-47-5.71-47-12.76,0-3.45,5.05-6.58,13.25-8.88"></path>
<path class="cls-3" d="M55.37,38.47a140,140,0,0,1,30.44-3c26,0,47,5.71,47,12.76,0,2.4-2.44,4.65-6.69,6.57"></path>
<path class="cls-3" d="M53.41,38.95l.94-.24"></path>
<path class="cls-4"
d="M91.68,47.71l-.75-11.2L79.15,43.84l-1.69,3.87H75.79c0,3.36,3.76,6.08,8.4,6.08s8.4-2.72,8.4-6.08Z"></path>
<path class="cls-5 neckMove"
d="M78,46.53a27.19,27.19,0,0,0,6.41.82c3.1,0,7.11-2.19,7.11-2.19l-.42-6.2L79.15,43.84Z"></path>
<polygon class="earMove" points="92.59 32.22 92.59 28.5 76.77 27.71 76.77 32.22 92.59 32.22"></polygon>
<circle class="cls-6 earMove" cx="78.06" cy="34.04" r="2.47"></circle>
<path class="cls-4"
d="M81.74,57.06,60.63,49.72h0A6.72,6.72,0,1,0,57.7,62.49H93.25C93.25,56.78,81.74,57.06,81.74,57.06Z"></path>
<path class="cls-4"
d="M77.46,25H90.92a0,0,0,0,1,0,0V39.38a6.73,6.73,0,0,1-6.73,6.73h0a6.73,6.73,0,0,1-6.73-6.73V25A0,0,0,0,1,77.46,25Z"></path>
<rect id="c-2" class="changeColor cls-7" x="74.82" y="26.48" width="19.14" height="2.45"
transform="translate(1.29 -3.65) rotate(2.49)" style="fill: rgb(255, 5, 18);"></rect>
<path id="c-3" class="changeColor cls-7"
d="M84.36,18.69h.5a7.8,7.8,0,0,1,7.8,7.8v0a0,0,0,0,1,0,0H76.56a0,0,0,0,1,0,0v0A7.8,7.8,0,0,1,84.36,18.69Z"
transform="translate(1.06 -3.66) rotate(2.49)" style="fill: rgb(255, 5, 18);"></path>
<polygon id="c-4" class="changeColor cls-8"
points="82.44 23.89 92.18 24.32 92.59 24.34 92.48 26.84 80.96 26.33 82.44 23.89"
style="fill: rgb(255, 5, 18);"></polygon>
<circle class="cls-9 faceMove" cx="78.72" cy="23.73" r="3.73"
transform="translate(51.58 101.34) rotate(-87.51)"></circle>
<circle class="cls-2 faceMove" cx="78.72" cy="23.73" r="2.36"
transform="translate(51.58 101.34) rotate(-87.51)"></circle>
<circle class="cls-4 earMove" cx="90.92" cy="34.04" r="2.47"></circle>
<path class="cls-4"
d="M112.2,53l-9.87-21.92-3-5.48-11.86-.22,7.42,3.35H91.55l5.82,4.58,2,22.26h0A6.72,6.72,0,1,0,112.2,53Z"></path>
<ellipse class="faceMove" cx="80.09" cy="33.12" rx="0.53" ry="0.59"></ellipse>
<ellipse class="faceMove" cx="86.34" cy="33.12" rx="0.53" ry="0.59"></ellipse>
<polyline class="cls-10 faceMove" points="84.19 31.08 81.74 37.01 84.39 37.01"></polyline>
<path class="cls-10 faceMove" d="M83.06,40.36a4,4,0,0,1,2.75-1"></path>
<line class="cls-11 faceMove" x1="81.07" y1="30.33" x2="78.47" y2="30.58"></line>
<line class="cls-11 faceMove" x1="86.34" y1="30.45" x2="88.15" y2="31.08"></line>
<line class="cls-12" x1="106.86" y1="47.82" x2="110.99" y2="46.11"></line>
<line class="cls-12" x1="107.43" y1="49.9" x2="111.55" y2="48.19"></line>
<line class="cls-12" x1="107.99" y1="51.98" x2="112.11" y2="50.27"></line>
<g class="cls-13">
<rect class="cls-14" x="85.81" y="2.46" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="96.58" y="2.46" width="10.77" height="3.5"></rect>
<rect class="cls-14" x="92.19" y="5.95" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="107.36" y1="5.95" x2="109.63" y2="5.95"></line>
<line class="cls-16" x1="110.68" y1="5.95" x2="111.57" y2="5.95"></line>
</g>
<g class="cls-13">
<rect class="cls-16" x="125" y="23.12" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="130.39" y="26.62" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="119.62" y="26.62" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="141.16" y1="26.62" x2="145.73" y2="26.62"></line>
<line class="cls-16" x1="125" y1="23.12" x2="115.4" y2="23.12"></line>
<line class="cls-16" x1="117.95" y1="26.62" x2="115.4" y2="26.62"></line>
</g>
<g class="cls-13">
<rect class="cls-16" x="39.34" y="16.12" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="39.34" y="23.11" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="50.11" y="23.11" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="50.11" y="16.12" width="10.77" height="3.5"></rect>
<rect class="cls-15" x="44" y="19.61" width="10.77" height="3.5"></rect>
<rect class="cls-16" x="33.23" y="19.61" width="10.77" height="3.5"></rect>
<line class="cls-16" x1="60.89" y1="19.61" x2="65.51" y2="19.61"></line>
<line class="cls-16" x1="39.34" y1="16.12" x2="35.46" y2="16.12"></line>
<line class="cls-16" x1="36.45" y1="26.61" x2="33.23" y2="26.61"></line>
<line class="cls-16" x1="63.2" y1="23.11" x2="65.51" y2="23.11"></line>
</g>
<polyline class="cls-3" points="115.4 58.12 115.4 38.27 120.2 37.01"></polyline>
<polyline class="cls-3" points="129.01 53.21 129.01 43.14 131.74 42.13"></polyline>
<path class="cls-3" d="M115.4,42.13a53.27,53.27,0,0,1,8,2A42,42,0,0,1,129,47"></path>
<path class="cls-3" d="M115.4,47.34a53.27,53.27,0,0,1,8,2A42,42,0,0,1,129,52.22"></path>
<path class="cls-3" d="M115.4,52.56a53.27,53.27,0,0,1,8,2l1,.42"></path>
<path class="cls-18 faceMove"
d="M78.84,26.09l0-4.71L68.05,18.32a.91.91,0,0,0-.45-.13c-1.17,0-2.11,2.46-2.11,5.5s.95,5.5,2.11,5.5a.9.9,0,0,0,.44-.12Z"></path>
<path class="cls-5" d="M57.7,62.49H93.25A3.67,3.67,0,0,0,92.92,61H53.43A6.69,6.69,0,0,0,57.7,62.49Z"></path>
<path class="cls-12" d="M88.15,60.27s1.7.95,1.7,2.22"></path>
<path class="cls-5" d="M101.81,61a6.68,6.68,0,0,0,8.51,0Z"></path>
<polygon class="cls-5" points="90.92 30.25 77.46 29.69 77.46 28.64 90.92 29.22 90.92 30.25"></polygon>
<text id="title" transform="matrix(1 0 0 1 44.7249 78)">Oops, something went wrong</text>
</svg> </svg>
<!-- <h1 id="title">404 Page not found</h1> -->
<!-- <button id="newBtn" class="btn" disabled>&#8594; &nbsp;Back to Home</button> -->

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -9,10 +9,8 @@
<nav th:replace="fragments/header :: header">Header</nav> <nav th:replace="fragments/header :: header">Header</nav>
<main> <main>
<div class='content-width'> <div class='content-width'>
<h1>Error 404</h1> <h1>Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden</h1>
<div> <div>
<p>Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden.</p>
<!-- animation from https://github.com/blairlee227/IlluStatus, licensed MIT -->
<img th:src="@{/img/error_404_illustatus.svg}"/> <img th:src="@{/img/error_404_illustatus.svg}"/>
</div> </div>
</div> </div>

View File

@ -9,10 +9,8 @@
<nav th:replace="fragments/header :: header">Header</nav> <nav th:replace="fragments/header :: header">Header</nav>
<main> <main>
<div class='content-width'> <div class='content-width'>
<h1>Error 500</h1> <h1>Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.</h1>
<div> <div>
<p>Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.</p>
<!-- animation from https://github.com/blairlee227/IlluStatus, licensed MIT -->
<img th:src="@{/img/error_generic_illustatus.svg}"/> <img th:src="@{/img/error_generic_illustatus.svg}"/>
</div> </div>
</div> </div>

View File

@ -26,7 +26,7 @@
</form> </form>
</div> </div>
</div> </div>
<a class="button" th:href="@{/shop/checkout}">Warenkorb</a> <a class="button" th:href="@{/shop/checkout}">Warenkorb (<span th:text="${shoppingCart.itemCount}"></span>)</a>
</div> </div>
<div th:if="${error}" class="error" id="error-msg"> <div th:if="${error}" class="error" id="error-msg">
<div class="content-width bar-flex"> <div class="content-width bar-flex">

View File

@ -19,32 +19,29 @@
<div class="detailgrid"> <div class="detailgrid">
<div class="s"> <div class="s">
<h1>Tolle Kamera</h1> <h1 th:text="${article.title}"></h1>
<script th:src="@{/js/back.js}"></script> <script th:src="@{/js/back.js}"></script>
<div class="back" data-group="shop" data-insert="true"></div> <div class="back" data-group="shop" data-insert="true"></div>
<h2>25.14&nbsp;EUR</h2> <h2><span th:text="${#numbers.formatDecimal(article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span><span> EUR</span></h2>
<p> <p th:text="${article.description}"></p>
Eine TOLLE Kamera <br>
Jaja du denkst jetzt bestimmt: "Bei dem Preis kann sie gar nich sooo TOLL sein". <br>
Aber glaub mir, sie is echt echt TOLL! <br>
Indianerehrenwort!
</p>
</div> </div>
<div class="s"> <div class="s">
<img th:src="@{/img/product-1.jpg}"/> <img th:src="@{/shop/articles/{id}/image.jpg(id=${article.id})}"/>
</div> </div>
<div class="s"></div> <div class="s"></div>
<form class="s" method="POST"> <form class="s" method="POST">
<div class="detailgrid m"> <div class="detailgrid m">
<h2>50.28&nbsp;EUR</h2> <h2><span th:text="${#numbers.formatDecimal(article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span><span> EUR</span></h2>
<div> <div>
<label class="nolinebreak">Menge:</label> <label class="nolinebreak">Menge:</label>
<select size="1"> <select name="quantity" size="1">
<option>2</option> <option th:each="quantity : ${#numbers.sequence(1,10)}"
th:value="${quantity}"
th:text="${quantity}"></option>
</select> </select>
</div> </div>
<h3 class="no-margin secondarytext">Auf Lager</h3> <h3 class="no-margin secondarytext" th:text="${inStock} ? 'AUF LAGER' : 'NICHT AUF LAGER'"></h3>
<button class="no-margin secondary" name="fastcheckout" value="false">In den Einkaufswagen <button class="no-margin secondary" name="fastcheckout" value="false">In den Einkaufswagen
</button> </button>
<button class="no-margin" name="fastcheckout" value="true">Schneller Checkout</button> <button class="no-margin" name="fastcheckout" value="true">Schneller Checkout</button>
@ -56,26 +53,13 @@
<div class="sidebar-layout"> <div class="sidebar-layout">
<div></div> <div></div>
<div> <div>
<h1>Weitere Schnäpchen</h1> <h1>Weitere Schnäppchen</h1>
<div class='grid m base shadow'> <div class='grid m base shadow'>
<section><a th:href="@{/shop/articles/1234}" class="section"> <section th:each="article: ${commercialArticles}"><a th:href="@{/shop/articles/{id}(id = ${article.id})}" class="section">
<img th:src="@{/img/product-4.jpg}"> <img th:src="@{/shop/articles/{id}/image.jpg(id=${article.id})}"/>
<h2>Kamera Stativ.</h2> <h2 th:text="${article.title}"></h2>
<p class='price'> 25.14&nbsp;EUR</p> <p class='price'><span th:text="${#numbers.formatDecimal(article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span><span> EUR</span></p>
<p> <p th:text="${article.description}"></p>
Das Stativ der Zukunft! Jetzt kaufen und verwenden für
wackelfreie Bilder aus der Zukunft!.
</p>
</a>
</section>
<section><a th:href="@{/shop/articles/1234}" class="section">
<img th:src="@{/img/product-5.jpg}">
<h2>Bluetooth Ersatzfernbedinung</h2>
<p class='price'> 10.14&nbsp;EUR</p>
<p>
Kann alles und jeden ausknippsen.
</p>
</a> </a>
</section> </section>
</div> </div>

View File

@ -21,7 +21,17 @@
</div> </div>
<nav></nav> <nav></nav>
</div> </div>
<main class="content-width sidebar-layout" style="min-height: 100vh;"> <main th:if="${checkoutItems.size() == 0}">
<div class="detailflex m">
<h2> Noch keine Artikel im Warenkorb. </h2>
<p>
<img th:src="@{/img/undraw_successful_purchase_secondary.svg}"/>
</p>
<a class="button" th:href="@{/}"> Weiter shoppen </a>
</div>
</main>
<main th:if="${checkoutItems.size() > 0}" class="content-width sidebar-layout" style="min-height: 100vh;">
<div style="max-width: 45em; width: 100%;"> <div style="max-width: 45em; width: 100%;">
<table> <table>
<tr> <tr>
@ -31,91 +41,32 @@
<th>Menge</th> <th>Menge</th>
<th></th> <th></th>
</tr> </tr>
<tr> <th:block th:each="item : ${checkoutItems}">
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-1.jpg}" class="s"/><a/></td> <tr>
<td><a th:href="@{/shop/articles/4151}">Kamera<a/></td> <td><a th:href="@{/shop/articles/{id}(id = ${item.article.id})}"><img
<td>100,50&nbsp;EUR</td> th:src="@{/shop/articles/{id}/image.jpg(id=${item.article.id})}" class="s"/></a></td>
<td>
<select size="1"> <td><a th:href="@{/shop/articles/{id}(id = ${item.article.id})}"
<option value="1" selected>1</option> th:text="${item.article.title}"></a></td>
<option value="2">2</option> <td><span
<option value="3">3</option> th:text="${#numbers.formatDecimal(item.article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span>
<option value="4">4</option> EUR <b>x</b></td>
<option value="5">5</option> <td>
</select> <form method="POST" th:action="@{/shop/articles/{id}(id = ${item.article.id})}">
</td> <input type="hidden" name="fastcheckout" value="true"/>
<td> <input type="hidden" name="set_amount" value="true"/>
<button class="small">Entfernen</button> <select name="quantity" size="1">
</td> <option th:value="${item.amount}" th:text="${item.amount}" selected></option>
</tr> <option value="0">Entfernen</option>
<tr> <option th:each="quantity : ${#numbers.sequence(1,10)}"
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-2.jpg}" class="s"/><a/></td> th:value="${quantity}"
<td><a th:href="@{/shop/articles/4151}">Earbuds<a/></td> th:text="${quantity}"></option>
<td>63,95&nbsp;EUR</td> </select>
<td> <button class="small">Ändern</button>
<select size="1"> </form>
<option value="1" selected>1</option> </td>
<option value="2">2</option> </tr>
<option value="3">3</option> <th:block>
<option value="4">4</option>
<option value="5">5</option>
</select>
</td>
<td>
<button>Entfernen</button>
</td>
</tr>
<tr>
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-3.jpg}" class="s"/><a/></td>
<td><a th:href="@{/shop/articles/4151}">USB-Magic Light<a/></td>
<td>11,90&nbsp;EUR</td>
<td>
<select size="1">
<option value="1">1</option>
<option value="2" selected>2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</td>
<td>
<button>Entfernen</button>
</td>
</tr>
<tr>
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-4.jpg}" class="s"/><a/></td>
<td><a th:href="@{/shop/articles/4151}">3D Magic Stativ<a/></td>
<td>15,99&nbsp;EUR</td>
<td>
<select size="1">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5" selected>5</option>
</select>
</td>
<td>
<button>Entfernen</button>
</td>
</tr>
<tr>
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-5.jpg}" class="s"/><a/></td>
<td><a th:href="@{/shop/articles/4151}">Ersatzfernbedinung<a/></td>
<td>7,95&nbsp;EUR</td>
<td>
<select size="1">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</td>
<td>
<button>Entfernen</button>
</td>
</tr>
</table> </table>
</div> </div>
<form method="POST" th:action="@{/shop/checkoutFinish}" style="min-width: 30em; max-width: 45em;"> <form method="POST" th:action="@{/shop/checkoutFinish}" style="min-width: 30em; max-width: 45em;">
@ -124,11 +75,16 @@
<h1>Checkout</h1> <h1>Checkout</h1>
</div> </div>
<div th:if="${user}"> <div th:if="${user}">
<input type="hidden" name="shopping_cart_revision" th:value="${shoppingCart.getRevision()}"/>
<input type="hidden" name="expected_total" th:value="${checkoutTotals.total}"/>
<h2>Lieferadresse:</h2> <h2>Lieferadresse:</h2>
<textarea rows="5" class="full-width" type="text" name="address" <textarea rows="5" class="full-width" type="text" name="address"
placeholder="Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land"> placeholder="Name&#10;Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land"
th:text="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress.toString() : ''}"
required>
Max Mustermann Max Mustermann
Musterstraße 42 Musterstraße 4
42424 Mustertal 42424 Mustertal
</textarea> </textarea>
</div> </div>
@ -138,12 +94,10 @@ Musterstraße 42
<input type="radio" name="type" value="priv" id="payment-card" required checked> <input type="radio" name="type" value="priv" id="payment-card" required checked>
<label for="payment-card">Kartenzahlung</label> <br/> <label for="payment-card">Kartenzahlung</label> <br/>
<input class="full-width" type="text" id="cardnumber" name="cardnumber" placeholder="Kartennummer" <input class="full-width" type="text" id="cardnumber" name="cardnumber" placeholder="Kartennummer"
th:value="${user.defaultPayment != null ? user.defaultPayment.creditCardNumber : ''}"
pattern="[0-9]{6,16}"
required/> required/>
</fieldset> </fieldset>
<fieldset>
<input class="" type="checkbox" id="bonus" name="bonus" checked/>
<label for="bonus"><h3>10 gesammelte Bonuspunkte verwenden</h3></label>
</fieldset>
</div> </div>
<div> <div>
<h2>Bestellübersicht</h2> <h2>Bestellübersicht</h2>
@ -151,23 +105,26 @@ Musterstraße 42
<table> <table>
<tr> <tr>
<th>Artikel (Netto)</th> <th>Artikel (Netto)</th>
<th>200,29&nbsp;EUR</th> <th><span
</tr> th:text="${#numbers.formatDecimal(checkoutTotals.net * 0.01, 1, 'POINT', 2, 'COMMA')}"></span>
<tr th:if="${user}"> EUR
<th>Bonuspunkte</th> </th>
<th>-5,00&nbsp;EUR</th>
</tr>
<tr>
<th>Umsatzsteuer (19%)</th>
<th>35,00&nbsp;EUR</th>
</tr>
<tr>
<th>Umsatzsteuer (7%)</th>
<th>2,50&nbsp;EUR</th>
</tr> </tr>
<th:block th:each="item : ${checkoutTotals.vatAmounts.entrySet()}">
<tr>
<th>Umsatzsteuer (<span th:text="${item.getKey()}"></span>%)</th>
<th><span
th:text="${#numbers.formatDecimal(item.getValue() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span>
EUR
</th>
</tr>
</th:block>
<tr class="secondary"> <tr class="secondary">
<th>Gesamt:</th> <th>Gesamt:</th>
<th>240,79&nbsp;EUR</th> <th><span
th:text="${#numbers.formatDecimal(checkoutTotals.total * 0.01, 1, 'POINT', 2, 'COMMA')}"></span>
EUR
</th>
</tr> </tr>
<tr th:if="${user}" class="secondary"> <tr th:if="${user}" class="secondary">
<th colspan="2" class=" no-padding"></th> <th colspan="2" class=" no-padding"></th>

View File

@ -17,55 +17,17 @@
<h1>Angebote</h1> <h1>Angebote</h1>
<script th:src="@{/js/back.js}"></script> <script th:src="@{/js/back.js}"></script>
<div class="back" data-group="shop" data-name="Zurück zur Startseite." data-insert="false"></div> <div class="back" data-group="shop" data-name="Zurück zur Startseite." data-insert="false"></div>
<div class='grid m base shadow'> <div th:if="${commercialArticles.size() == 0}">
<section><a th:href="@{/shop/articles/1234}" class="section"> <h1>Momentan gibt es keine Angebote</h1>
</div>
<div class='grid m base shadow' th:if="${commercialArticles.size() != 0}">
<section th:each="article: ${commercialArticles}">
<a th:href="@{/shop/articles/{id}(id=${article.id})}" class="section">
<img th:src="@{/img/product-1.jpg}"/> <img th:src="@{/shop/articles/{id}/image.jpg(id=${article.id})}"/>
<h2>Lorem Ipsum</h2> <h2 th:text="${article.title}" />
<p class='price'> 25.14&nbsp;EUR</p> <p class='price'><span th:text="${#numbers.formatDecimal(article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span><span> EUR</span></p>
<p> <p th:text="${article.description}" />
Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte.
</p>
</a>
</section>
<section><a th:href="@{/shop/articles/1234}" class="section">
<img th:src="@{/img/product-2.jpg}"/>
<h2>Lorem Ipsum</h2>
<p class='price'> 10.14&nbsp;EUR</p>
<p>
Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte.
</p>
</a>
</section>
<section><a th:href="@{/shop/articles/1234}" class="section">
<img th:src="@{/img/product-3.jpg}"/>
<h2>Lorem Ipsum</h2>
<p class='price'> 25.14&nbsp;EUR</p>
<p>
Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte.
</p>
</a>
</section>
<section><a th:href="@{/shop/articles/1234}" class="section">
<img th:src="@{/img/product-4.jpg}"/>
<h2>Lorem Ipsum</h2>
<p class='price'> 10.14&nbsp;EUR</p>
<p>
Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte.
</p>
</a>
</section>
<section>
<a th:href="@{/shop/articles/1234}" class="section">
<img th:src="@{/img/product-5.jpg}"/>
<h2>Lorem Ipsum</h2>
<p class='price'> 44.14&nbsp;EUR</p>
<p>
Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte.
</p>
</a> </a>
</section> </section>
<section class="spacer"></section> <section class="spacer"></section>
@ -79,7 +41,9 @@
<div class=''> <div class=''>
<div class='content-width'> <div class='content-width'>
<h1>Personalisierte Empfehlungen</h1> <h1>Personalisierte Empfehlungen</h1>
<div class="grid l">
<!-- if a User is NOT logged in-->
<div class="grid l" th:if="${isLoggedIn == false}">
<img th:src="@{/img/undraw_successful_purchase_secondary.svg}"/> <img th:src="@{/img/undraw_successful_purchase_secondary.svg}"/>
<div> <div>
<h2>Werde jetzt Kunde</h2> <h2>Werde jetzt Kunde</h2>
@ -90,6 +54,26 @@
</p> </p>
</div> </div>
</div> </div>
<!-- If User is logged in but hasn't ordered anything yet-->
<div th:if="${isLoggedIn == true and hasOrders == false}">
<h1>Jetzt Shoppen und Empfehlungen erhalten!</h1>
</div>
<!-- If User is logged in and has ordered something before-->
<div th:if="${hasOrders == true}">
<div class='grid m base shadow'>
<section th:each="article: ${suggestedArticles}">
<a th:href="@{/shop/articles/{id}(id=${article.id})}" class="section">
<img th:src="@{/shop/articles/{id}/image.jpg(id=${article.id})}"/>
<h2 th:text="${article.title}" />
<p class='price'><span th:text="${#numbers.formatDecimal(article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"></span><span> EUR</span></p>
<p th:text="${article.description}" />
</a>
</section>
</div>
</div>
</div> </div>
<div class="vertical-spacer s"></div> <div class="vertical-spacer s"></div>
</div> </div>