Added Article With Image
This commit is contained in:
@@ -24,6 +24,7 @@ public class ControllerPathConfig {
|
||||
//ArticleController
|
||||
public static final String ARTICLE_BASE = "/article";
|
||||
public static final String ARTICLE_GET_ALL = ARTICLE_BASE + "/all";
|
||||
public static final String ARTICLE_GET_ALL_WITH_IMAGE = ARTICLE_BASE + "/all/image";
|
||||
|
||||
//CustomerController
|
||||
public static final String CUSTOMER_BASE = "/customer";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.htwsaar.webshop.controller;
|
||||
|
||||
import de.htwsaar.webshop.model.ArticleModel;
|
||||
import de.htwsaar.webshop.model.ArticleWithImageModel;
|
||||
import de.htwsaar.webshop.repository.entities.Article;
|
||||
import de.htwsaar.webshop.service.ArticleService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -14,6 +15,7 @@ import java.util.UUID;
|
||||
|
||||
import static de.htwsaar.webshop.config.ControllerPathConfig.ARTICLE_BASE;
|
||||
import static de.htwsaar.webshop.config.ControllerPathConfig.ARTICLE_GET_ALL;
|
||||
import static de.htwsaar.webshop.config.ControllerPathConfig.ARTICLE_GET_ALL_WITH_IMAGE;
|
||||
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_UUID;
|
||||
import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
|
||||
|
||||
@@ -33,6 +35,12 @@ public class ArticleController {
|
||||
return ResponseEntity.ok(articleService.from(articleService.findAll()));
|
||||
}
|
||||
|
||||
@RequestMapping(path = ARTICLE_GET_ALL_WITH_IMAGE, method = RequestMethod.GET, produces = "application/json")
|
||||
public ResponseEntity<List<ArticleWithImageModel>> getAllWithImageData(HttpServletRequest request) {
|
||||
logRequest(request);
|
||||
return ResponseEntity.ok(articleService.fromWithImage(articleService.findAll()));
|
||||
}
|
||||
|
||||
@RequestMapping(path = ARTICLE_BASE, method = RequestMethod.GET, produces = "application/json")
|
||||
public ResponseEntity<ArticleModel> getByUUID(HttpServletRequest request,
|
||||
@RequestParam(value = PARAM_UUID) UUID uuid) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package de.htwsaar.webshop.model;
|
||||
|
||||
import de.htwsaar.webshop.repository.entities.Image;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* What the Frontend gets when requesting an Article, POJO
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Setter
|
||||
@Getter
|
||||
public class ArticleWithImageModel {
|
||||
private long id;
|
||||
private UUID uuid;
|
||||
private String name;
|
||||
private String description;
|
||||
private int price100;
|
||||
private int discount100;
|
||||
private int stock;
|
||||
private int stockExpected;
|
||||
private String category;
|
||||
private double rating;
|
||||
private String image;
|
||||
|
||||
public ArticleWithImageModel(ArticleModel article, Image image){
|
||||
this.id = article.getId();
|
||||
this.uuid = article.getUuid();
|
||||
this.name = article.getName();
|
||||
this.description = article.getDescription();
|
||||
this.price100 = article.getPrice100();
|
||||
this.discount100 = article.getDiscount100();
|
||||
this.stock = article.getStock();
|
||||
this.stockExpected = article.getStockExpected();
|
||||
this.category = article.getCategory();
|
||||
this.rating = article.getRating();
|
||||
this.image = image.getBase64();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.htwsaar.webshop.service;
|
||||
|
||||
import de.htwsaar.webshop.model.ArticleModel;
|
||||
import de.htwsaar.webshop.model.ArticleWithImageModel;
|
||||
import de.htwsaar.webshop.repository.entities.Article;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,4 +34,6 @@ public interface ArticleService {
|
||||
* @return an <b>unmodifiable</b> {@link List} of {@link ArticleModel}s
|
||||
*/
|
||||
List<ArticleModel> from(List<Article> articles);
|
||||
|
||||
List<ArticleWithImageModel> fromWithImage(List<Article> articles);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package de.htwsaar.webshop.service.impl;
|
||||
|
||||
import de.htwsaar.webshop.model.ArticleModel;
|
||||
import de.htwsaar.webshop.model.ArticleWithImageModel;
|
||||
import de.htwsaar.webshop.repository.ArticleRepository;
|
||||
import de.htwsaar.webshop.repository.ImageRepository;
|
||||
import de.htwsaar.webshop.repository.ReviewRepository;
|
||||
import de.htwsaar.webshop.repository.entities.Article;
|
||||
import de.htwsaar.webshop.repository.entities.Review;
|
||||
import de.htwsaar.webshop.service.ArticleService;
|
||||
import de.htwsaar.webshop.service.ImageService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -19,11 +22,13 @@ import java.util.UUID;
|
||||
public class ArticleServiceImpl implements ArticleService {
|
||||
private final ArticleRepository articleRepository;
|
||||
private final ReviewRepository reviewRepository;
|
||||
private final ImageRepository imageService;
|
||||
|
||||
@Autowired
|
||||
public ArticleServiceImpl(ArticleRepository articleRepository, ReviewRepository reviewRepository) {
|
||||
public ArticleServiceImpl(ArticleRepository articleRepository, ReviewRepository reviewRepository, ImageRepository imageService) {
|
||||
this.articleRepository = articleRepository;
|
||||
this.reviewRepository = reviewRepository;
|
||||
this.imageService = imageService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,4 +112,10 @@ public class ArticleServiceImpl implements ArticleService {
|
||||
public List<ArticleModel> from(List<Article> articles) {
|
||||
return articles.stream().map(this::from).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ArticleWithImageModel> fromWithImage(List<Article> articles) {
|
||||
return articles.stream().map(this::from).map(
|
||||
(a) -> new ArticleWithImageModel(a, imageService.findImageByArticle_Uuid(a.getUuid()))).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
type Item = {
|
||||
export type Item = {
|
||||
id: number;
|
||||
uuid: string;
|
||||
name: string;
|
||||
@@ -11,4 +11,18 @@ type Item = {
|
||||
discount100: number;
|
||||
};
|
||||
|
||||
export default Item;
|
||||
type ItemWithImage = {
|
||||
id: number;
|
||||
uuid: string;
|
||||
name: string;
|
||||
description: string;
|
||||
price100: number;
|
||||
stock: number;
|
||||
stockExpected: number;
|
||||
category: string;
|
||||
rating: number;
|
||||
discount100: number;
|
||||
image: string;
|
||||
};
|
||||
|
||||
export default ItemWithImage;
|
||||
@@ -27,7 +27,7 @@ export default function ItemsInfo() {
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ["fetchItems", loginData],
|
||||
queryFn: () => fetchItems(loginData? loginData : {email: "", password: "", session: "", customerId: -1, isAdmin: false}),
|
||||
queryFn: () => fetchItems(),
|
||||
retry: 3,
|
||||
retryDelay: 1000,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { AddShoppingCart } from "@mui/icons-material";
|
||||
import { Box, Card, CardActionArea, CardContent, CardMedia, IconButton, Paper, Rating, Typography } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Item from "../../components/Item";
|
||||
import ItemWithImage from "../../components/Item";
|
||||
import { useBasket } from "../BasketProvider";
|
||||
import "../helper.css";
|
||||
|
||||
export default function ItemCard({ item }: { item: Item }) {
|
||||
export default function ItemCard({ item }: { item: ItemWithImage }) {
|
||||
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate()
|
||||
@@ -21,24 +21,7 @@ export default function ItemCard({ item }: { item: Item }) {
|
||||
const handleClick = () => {
|
||||
navigate(`/product/${item.id}`, { state: { item } });
|
||||
}
|
||||
const [imageUrl, setImageUrl] = useState<string>("/src/assets/default.jpg"); // Fallback-Bild
|
||||
|
||||
useEffect(() => {
|
||||
const fetchImage = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8085/image?uuid=${item.uuid}`); //image/* as base64
|
||||
var data = await response.text();
|
||||
if(!data.startsWith("data:image/")) {
|
||||
data = "data:image/jpeg;base64," + data
|
||||
}
|
||||
setImageUrl(data);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Laden des Bildes:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchImage();
|
||||
}, [item.uuid]);
|
||||
const [imageUrl] = useState<string>(item.image || "/src/assets/default.jpg"); // Fallback-Bild
|
||||
|
||||
|
||||
return (
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function ProductInfo({ item }: { item: Item }) {
|
||||
const fetchImage = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8085/image?uuid=${item.uuid}`);
|
||||
var data = await response.text();
|
||||
let data = await response.text();
|
||||
if(data.length == 0) {
|
||||
console.error("Got emtpy picture for article ", item.uuid);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,15 @@ export const fetchItemList = async () => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const fetchItemListWithImage = async () => {
|
||||
const response = await fetch('http://localhost:8085/article/all/image');
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Laden der Items');
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
export const submitRating = async (ratingData: RatingSubmitType) => {
|
||||
const response = await fetch('http://localhost:8085/review?uuid=' + ratingData.articleId + '&rating=' + ratingData.rating * 2, {
|
||||
method: 'POST',
|
||||
@@ -159,7 +168,7 @@ export const fetchAccounts = async (loginData: User) => {
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const fetchItems = async (loginData: User) => { //TODO: remove and use above
|
||||
export const fetchItems = async () => { //TODO: remove and use above
|
||||
const response = await fetch("http://localhost:8085/article/all");
|
||||
if (!response.ok) {
|
||||
throw new Error("fetching items failed");
|
||||
|
||||
@@ -3,11 +3,11 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import Item from "../components/Item";
|
||||
import ItemWithImage from "../components/Item";
|
||||
import FilterItem from "../helper/homepage/FilterItem";
|
||||
import ItemCard from "../helper/homepage/ItemCard";
|
||||
import PriceSlider from "../helper/homepage/PriceSlider";
|
||||
import { fetchItemList } from '../helper/query/Queries';
|
||||
import { fetchItemListWithImage } from '../helper/query/Queries';
|
||||
import "./pages.css"; // Import der CSS-Datei
|
||||
|
||||
export default function Home() {
|
||||
@@ -37,14 +37,14 @@ export default function Home() {
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [selectedRating, setSelectedRating] = useState<string | null>(null);
|
||||
|
||||
const { data = [] } = useQuery<Item[]>({
|
||||
queryKey: ['fetchItemList'],
|
||||
queryFn: fetchItemList,
|
||||
const { data = [] } = useQuery<ItemWithImage[]>({
|
||||
queryKey: ['fetchItemListWithImage'],
|
||||
queryFn: fetchItemListWithImage,
|
||||
retry: 3, // Versucht es 3-mal erneut
|
||||
retryDelay: 1000, // Wartezeit zwischen den Versuchen (in ms)
|
||||
});
|
||||
|
||||
const items:Item[] = useMemo(() => data || [], [data]);
|
||||
const items:ItemWithImage[] = useMemo(() => data || [], [data]);
|
||||
|
||||
const discountedPrices = items.map(
|
||||
(item) => item.price100 * (1 - item.discount100 / 100)
|
||||
@@ -68,7 +68,7 @@ export default function Home() {
|
||||
}, [location.search, categoriesFilter]);
|
||||
|
||||
// Filterfunktion bleibt gleich
|
||||
const filteredItems = useMemo(() => {return items
|
||||
const filteredItems: ItemWithImage[] = useMemo(() => {return items
|
||||
.filter((item) => {
|
||||
const discountedPrice = item.price100 * (1 - item.discount100 / 100);
|
||||
return discountedPrice >= priceRange[0] && discountedPrice <= priceRange[1];
|
||||
@@ -100,7 +100,7 @@ export default function Home() {
|
||||
|
||||
|
||||
// Items, die aktuell angezeigt werden
|
||||
const visibleItems: Item[] = filteredItems;
|
||||
const visibleItems: ItemWithImage[] = filteredItems;
|
||||
|
||||
// Container Ref
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
Reference in New Issue
Block a user