diff --git a/frontend/style/components/cart-summary.tsx b/frontend/style/components/cart-summary.tsx index a41f68f2..8563fb21 100644 --- a/frontend/style/components/cart-summary.tsx +++ b/frontend/style/components/cart-summary.tsx @@ -42,9 +42,9 @@ export function CartSummary() { Итого {totalPrice} ₽ - + */} ) } diff --git a/frontend/style/components/header.tsx b/frontend/style/components/header.tsx index c5c89da2..ebd10fc1 100644 --- a/frontend/style/components/header.tsx +++ b/frontend/style/components/header.tsx @@ -2,6 +2,7 @@ import { useState } from "react" import Link from "next/link" +import { useRouter } from "next/navigation" import { Search } from "./search" import { Button } from "./ui/button" import { ShoppingCart, Heart, User, Menu, X } from "lucide-react" @@ -13,19 +14,24 @@ import { useAuth } from "@/contexts/auth-context" import { Badge } from "./ui/badge" import { CartSummary } from "./cart-summary" - export function Header() { const [isMenuOpen, setIsMenuOpen] = useState(false) const [isCartOpen, setIsCartOpen] = useState(false) const { getTotalItems, getTotalUniqueItems } = useCart() const { getTotalFavorites } = useFavorites() const { isLoggedIn } = useAuth() + const router = useRouter() + + const handleNavigate = (path: string) => { + router.push(path) + setIsMenuOpen(false) // Закрываем меню после навигации + } return (
-
+
-
+
STORE @@ -56,10 +62,19 @@ export function Header() { )} - + + + + + {/* +

Корзина

@@ -83,12 +106,13 @@ export function Header() {
+ - + */}
@@ -97,34 +121,34 @@ export function Header() { - -
-

Меню

- - - -
+
+ + + diff --git a/frontend/style/components/product-detail.tsx b/frontend/style/components/product-detail.tsx index f1cc4b65..32810479 100644 --- a/frontend/style/components/product-detail.tsx +++ b/frontend/style/components/product-detail.tsx @@ -2,11 +2,11 @@ import Image from "next/image" import { useState } from "react" -import { Heart, ShoppingCart, Minus, Plus } from 'lucide-react' +import { Heart, ShoppingCart, Minus, Plus } from "lucide-react" import { Button } from "./ui/button" import { useCart } from "@/contexts/cart-context" import { useFavorites } from "@/contexts/favorites-context" -import { Product } from "@/types/product" +import type { Product } from "@/types/product" import { ReviewList } from "./review-list" import { ReviewForm } from "./review-form" import { useAuth } from "@/contexts/auth-context" @@ -22,15 +22,18 @@ export function ProductDetail({ product }: ProductDetailProps) { const { addToFavorites, removeFromFavorites, isFavorite } = useFavorites() const { isLoggedIn } = useAuth() - const isInCart = items.some(item => item.id === product.id) + const isInCart = items.some((item) => item.id === product.id) const handleAddToCart = () => { if (!isInCart) { - addToCart({ - id: product.id, - title: product.title, - price: product.price, - }, quantity) + addToCart( + { + id: product.id, + title: product.title, + price: product.price, + }, + quantity, + ) } } @@ -51,7 +54,7 @@ export function ProductDetail({ product }: ProductDetailProps) {
{product.title}

{product.title}

- {product.category === 'software' && ( + {product.category === "software" && (
- Тип лицензии: {product.licenseType === 'perpetual' ? 'Бессрочная' : 'Подписка'} + Тип лицензии: {product.licenseType === "perpetual" ? "Бессрочная" : "Подписка"}
)}
@@ -72,19 +75,11 @@ export function ProductDetail({ product }: ProductDetailProps) { {!isInCart && ( <>
- {quantity} -
@@ -99,13 +94,14 @@ export function ProductDetail({ product }: ProductDetailProps) { )}

Описание

- {product.description || 'Подробное описание товара. Здесь может быть длинный текст с характеристиками и особенностями продукта.'} + {product.description || + "Подробное описание товара. Здесь может быть длинный текст с характеристиками и особенностями продукта."}

@@ -114,14 +110,20 @@ export function ProductDetail({ product }: ProductDetailProps) {

Отзывы

{isLoggedIn ? ( <> - +

Оставить отзыв

) : ( -

Чтобы просматривать и оставлять отзывы, пожалуйста, войдите в систему.

+

+ Чтобы просматривать и оставлять отзывы, пожалуйста,{" "} + + войдите в систему + + . +

)}
diff --git a/frontend/style/components/review-form.tsx b/frontend/style/components/review-form.tsx index a376ca89..ca7c95bc 100644 --- a/frontend/style/components/review-form.tsx +++ b/frontend/style/components/review-form.tsx @@ -17,20 +17,19 @@ export function ReviewForm({ productId }: ReviewFormProps) { const [rating, setRating] = useState(0) const [comment, setComment] = useState("") const { isLoggedIn } = useAuth() - const {email} = useAuth(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() const reviewData = { - username: {email}=useAuth(), // Здесь можно подтянуть имя из Auth-контекста + username: "No name", // Здесь можно подтянуть имя из Auth-контекста rating, comment, } try { - const response = await fetch(`http://localhost:8080/product/1`, { + const response = await fetch(`http://localhost:8080/product/${productId}`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/style/components/review-list.tsx b/frontend/style/components/review-list.tsx index 6fbdd7fc..7f94567a 100644 --- a/frontend/style/components/review-list.tsx +++ b/frontend/style/components/review-list.tsx @@ -1,10 +1,20 @@ +"use client" + import { useEffect, useState } from "react" -import type { Review } from "@/types/product" import { Star } from "lucide-react" import { useAuth } from "@/contexts/auth-context" import Link from "next/link" import { Button } from "./ui/button" +interface Review { + id: number + product_id: number + username: string + rating: number + comment: string + createdAt: string +} + interface ReviewListProps { productId: number } @@ -12,34 +22,41 @@ interface ReviewListProps { export function ReviewList({ productId }: ReviewListProps) { const { isLoggedIn } = useAuth() const [reviews, setReviews] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) useEffect(() => { const fetchReviews = async () => { + setIsLoading(true) + setError(null) try { - const response = await fetch(`http://localhost:8080/product/1`) - if (!response.ok) throw new Error("Ошибка загрузки отзывов") - + const response = await fetch(`http://localhost:8080/product/${productId}`) + + // Добавляем логирование для отладки + console.log("Response status:", response.status) const data = await response.json() - console.log("Загруженные отзывы:", data) // Check the data received - setReviews(data) + console.log("Fetched reviews:", data) + + // Проверяем, является ли data массивом + if (Array.isArray(data)) { + setReviews(data) + } else { + setReviews([]) + } } catch (error) { - console.error(error) + console.error("Error fetching reviews:", error) + setError("Ошибка при загрузке отзывов") + setReviews([]) + } finally { + setIsLoading(false) } } - - fetchReviews() + + if (productId) { + fetchReviews() + } }, [productId]) - - - interface Review { - id: number; - product_id: number; - username: string; - rating: number; - comment: string; - createdAt: string; - } - + if (!isLoggedIn) { return (
@@ -51,16 +68,23 @@ export function ReviewList({ productId }: ReviewListProps) { ) } + if (isLoading) { + return
Загрузка отзывов...
+ } + + if (error) { + return
{error}
+ } + return (

Отзывы покупателей

- {reviews.length === 0 ? ( + {!reviews || reviews.length === 0 ? (

Пока нет отзывов. Будьте первым!

) : ( reviews.map((review) => (
- {/* Вывод рейтинга с помощью звезд */}
{[1, 2, 3, 4, 5].map((star) => ( ))}
- {/* Дата отзыва */} {new Date(review.createdAt).toLocaleDateString()}
- {/* Комментарий */} +

{review.username}

{review.comment}

)) )}
) - } + diff --git a/frontend/style/components/reviews.db b/frontend/style/components/reviews.db index 31720121..fba9a53b 100644 Binary files a/frontend/style/components/reviews.db and b/frontend/style/components/reviews.db differ diff --git a/frontend/style/components/reviews.go b/frontend/style/components/reviews.go index bccb2c72..c3830071 100644 --- a/frontend/style/components/reviews.go +++ b/frontend/style/components/reviews.go @@ -3,7 +3,6 @@ package main import ( "database/sql" "encoding/json" - "fmt" "log" "net/http" "strconv" @@ -65,12 +64,65 @@ func createTable() { log.Fatal("Ошибка создания таблицы:", err) } } +func addReview(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + productID, err := strconv.Atoi(vars["product_id"]) + if err != nil { + http.Error(w, "Некорректный product_id", http.StatusBadRequest) + log.Println("Ошибка преобразования product_id:", err) + return + } + + var review Review + if err := json.NewDecoder(r.Body).Decode(&review); err != nil { + http.Error(w, "Некорректный JSON", http.StatusBadRequest) + log.Println("Ошибка декодирования JSON:", err) + return + } + + // Игнорируем переданный в JSON product_id и устанавливаем его из URL + review.ProductID = productID + log.Printf("Добавление отзыва для товара %d: %+v", productID, review) + + stmt, err := db.Prepare(` + INSERT INTO reviews (product_id, username, rating, comment, created_at) + VALUES (?, ?, ?, ?, datetime('now')) + `) + if err != nil { + http.Error(w, "Ошибка при подготовке SQL-запроса", http.StatusInternalServerError) + log.Println("Ошибка при подготовке запроса:", err) + return + } + defer stmt.Close() + + result, err := stmt.Exec(review.ProductID, review.Username, review.Rating, review.Comment) + if err != nil { + http.Error(w, "Ошибка при добавлении отзыва", http.StatusInternalServerError) + log.Println("Ошибка при выполнении SQL-запроса:", err) + return + } + + rowsAffected, _ := result.RowsAffected() + log.Printf("Добавлено строк: %d для товара %d", rowsAffected, productID) + + // Возвращаем созданный отзыв + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(review) +} func getReviews(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) productID := vars["product_id"] - rows, err := db.Query("SELECT id, product_id, username, rating, comment, created_at FROM reviews WHERE product_id = ?", productID) + log.Printf("Получение отзывов для товара %s", productID) + + rows, err := db.Query(` + SELECT id, product_id, username, rating, comment, created_at + FROM reviews + WHERE product_id = ? + ORDER BY created_at DESC + `, productID) if err != nil { http.Error(w, "Ошибка при получении отзывов", http.StatusInternalServerError) log.Println("Ошибка при запросе отзывов:", err) @@ -89,55 +141,9 @@ func getReviews(w http.ResponseWriter, r *http.Request) { reviews = append(reviews, review) } - // Log the reviews being returned - log.Printf("Reviews fetched: %+v", reviews) - - if len(reviews) == 0 { - http.Error(w, "Нет отзывов", http.StatusNotFound) - return - } + log.Printf("Найдено %d отзывов для товара %s", len(reviews), productID) + // Всегда возвращаем JSON массив, даже если он пустой w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(reviews) } - -func addReview(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - productID, err := strconv.Atoi(vars["product_id"]) - if err != nil { - http.Error(w, "Некорректный product_id", http.StatusBadRequest) - log.Println("Ошибка преобразования product_id:", err) - return - } - - var review Review - if err := json.NewDecoder(r.Body).Decode(&review); err != nil { - http.Error(w, "Некорректный JSON", http.StatusBadRequest) - log.Println("Ошибка декодирования JSON:", err) - return - } - - review.ProductID = productID - log.Printf("Попытка добавить отзыв: %+v", review) - - stmt, err := db.Prepare("INSERT INTO reviews (product_id, username, rating, comment, created_at) VALUES (?, ?, ?, ?, datetime('now'))") - if err != nil { - http.Error(w, "Ошибка при подготовке SQL-запроса", http.StatusInternalServerError) - log.Println("Ошибка при подготовке запроса:", err) - return - } - defer stmt.Close() - - result, err := stmt.Exec(review.ProductID, review.Username, review.Rating, review.Comment) - if err != nil { - http.Error(w, "Ошибка при добавлении отзыва", http.StatusInternalServerError) - log.Println("Ошибка при выполнении SQL-запроса:", err) - return - } - - rowsAffected, _ := result.RowsAffected() - log.Println("Добавлено строк:", rowsAffected) - - w.WriteHeader(http.StatusCreated) - fmt.Fprintln(w, "Отзыв успешно добавлен") -}