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