mirror of
https://github.com/yawaflua/SusMarket.git
synced 2026-04-24 16:50:41 +03:00
Fixes & commentaries
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "/static/css/main.b9cb408a.css",
|
"main.css": "/static/css/main.b9cb408a.css",
|
||||||
"main.js": "/static/js/main.08d74509.js",
|
"main.js": "/static/js/main.d5c997c3.js",
|
||||||
"static/media/scam-image.png": "/static/media/scam-image.c6c14289dc251ba2d2b1.png",
|
"static/media/scam-image.png": "/static/media/scam-image.c6c14289dc251ba2d2b1.png",
|
||||||
"static/media/info-page__railth-avatar.png": "/static/media/info-page__railth-avatar.cbf11c43b5ef243b38c0.png",
|
"static/media/info-page__railth-avatar.png": "/static/media/info-page__railth-avatar.cbf11c43b5ef243b38c0.png",
|
||||||
"static/media/add.webp": "/static/media/add.cd69f1e2a8c91109db0f.webp",
|
"static/media/add.webp": "/static/media/add.cd69f1e2a8c91109db0f.webp",
|
||||||
@@ -14,10 +14,10 @@
|
|||||||
"static/media/rating__filled-star-icon.svg": "/static/media/rating__filled-star-icon.dc7d908d4d943b7f3b56.svg",
|
"static/media/rating__filled-star-icon.svg": "/static/media/rating__filled-star-icon.dc7d908d4d943b7f3b56.svg",
|
||||||
"index.html": "/index.html",
|
"index.html": "/index.html",
|
||||||
"main.b9cb408a.css.map": "/static/css/main.b9cb408a.css.map",
|
"main.b9cb408a.css.map": "/static/css/main.b9cb408a.css.map",
|
||||||
"main.08d74509.js.map": "/static/js/main.08d74509.js.map"
|
"main.d5c997c3.js.map": "/static/js/main.d5c997c3.js.map"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.b9cb408a.css",
|
"static/css/main.b9cb408a.css",
|
||||||
"static/js/main.08d74509.js"
|
"static/js/main.d5c997c3.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>SusMarket</title><link rel="manifest" href="/manifest.json"/><script defer="defer" src="/static/js/main.08d74509.js"></script><link href="/static/css/main.b9cb408a.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>SusMarket</title><link rel="manifest" href="/manifest.json"/><script defer="defer" src="/static/js/main.d5c997c3.js"></script><link href="/static/css/main.b9cb408a.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -60,7 +60,7 @@ export default function App() {
|
|||||||
{state.isPopupMapVisible && <PopupMap togglePopupMap={togglePopupMap}/>}
|
{state.isPopupMapVisible && <PopupMap togglePopupMap={togglePopupMap}/>}
|
||||||
<main className="main">
|
<main className="main">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<HomePage products={filteredProducts} selectedCategory={selectedCategory}/>}/>
|
<Route path="/" element={<HomePage products={filteredProducts}/>}/>
|
||||||
<Route path="profile/*" element={<ProfilePage />}/>
|
<Route path="profile/*" element={<ProfilePage />}/>
|
||||||
<Route path="product/:id" element={<ProductPage/>}/>
|
<Route path="product/:id" element={<ProductPage/>}/>
|
||||||
<Route path="payment" element={<PaymentPage />}/>
|
<Route path="payment" element={<PaymentPage />}/>
|
||||||
|
|||||||
@@ -8,21 +8,20 @@ interface CatalogMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CatalogMenu({ toggleCatalogMenu, onSelectCategory }: CatalogMenuProps): JSX.Element {
|
export default function CatalogMenu({ toggleCatalogMenu, onSelectCategory }: CatalogMenuProps): JSX.Element {
|
||||||
const [categories, setCategories] = useState<Category[]>([]);
|
const [categories, setCategories] = useState<Category[]>([]); //состояние для категорий
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => { //запрос к api для получения категорий
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://127.0.0.1:8000/api/get/category');
|
const response = await axios.get('http://127.0.0.1:8000/api/get/category');
|
||||||
setCategories(response.data.categories);
|
setCategories(response.data.categories);
|
||||||
console.log(response.data);
|
} catch (error) {
|
||||||
} catch (error) {
|
|
||||||
console.error(`There was an error retrieving the data: ${error}`);
|
console.error(`There was an error retrieving the data: ${error}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchCategories();
|
fetchCategories();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { DeveloperCard } from "../utils/types"
|
||||||
|
|
||||||
interface DeveloperCardProps {
|
export default function DevCard({ avatar, name, info, url }: DeveloperCard) {
|
||||||
avatar: string;
|
|
||||||
name: string;
|
|
||||||
info: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function DevCard({ avatar, name, info, url }: DeveloperCardProps) {
|
|
||||||
return (
|
return (
|
||||||
<div className="info-page__dev-card">
|
<div className="info-page__dev-card">
|
||||||
<div className="dev-card__inner">
|
<div className="dev-card__inner">
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ type ReviewProps = {
|
|||||||
review: Reviews;
|
review: Reviews;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Review({ review }: ReviewProps) {
|
export default function Review({ review }: ReviewProps) { //соответствие типов данных в review с указанными типами и свойствами в интерфейсе Reviews
|
||||||
const readableDate = new Date(review.date).toLocaleDateString('ru-RU');
|
const readableDate = new Date(review.date).toLocaleDateString('ru-RU'); //приводит дату отзыва из запроса к api в читаемый вид (чч/мм/гг)
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<article className="review-article">
|
<article className="review-article">
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import React from "react";
|
|||||||
import '../HomeStyle.scss';
|
import '../HomeStyle.scss';
|
||||||
import ProductCard from "../components/ProductCard";
|
import ProductCard from "../components/ProductCard";
|
||||||
import Banner from "../components/AdBanner";
|
import Banner from "../components/AdBanner";
|
||||||
import { Product, Category } from "../utils/types";
|
import { Product } from "../utils/types";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
interface HomePageProps {
|
type HomePageProps = {
|
||||||
products: Product[];
|
products: Product[];
|
||||||
selectedCategory: Category | 'all';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HomePage({ products, selectedCategory }: HomePageProps) {
|
export default function HomePage({ products }: HomePageProps) { //соответствие типов данных в products с указанными типами и свойствами в интерфейсе Product
|
||||||
return(
|
return(
|
||||||
<section className="home-page">
|
<section className="home-page">
|
||||||
<Banner />
|
<Banner />
|
||||||
|
|||||||
@@ -2,40 +2,36 @@ import React, { useState } from "react";
|
|||||||
import '../PaymentStyle.scss';
|
import '../PaymentStyle.scss';
|
||||||
|
|
||||||
export default function PaymentPage() {
|
export default function PaymentPage() {
|
||||||
let price = 150
|
const [ccNumber, setCcNumber] = useState(""); //состояние номера кредитной карты
|
||||||
|
const [valueDate, setValueDate] = useState<number | ''>(''); //состояние даты истечения срока карты
|
||||||
|
const [valueCode, setValueCode] = useState<number | ''>(''); //состояние кода безопасности карты
|
||||||
|
|
||||||
const [ccNumber, setCcNumber] = useState("");
|
const formatAndSetCcNumber = (e: React.ChangeEvent<HTMLInputElement>) => { //в ccNumber максимум 16 цифр, разбивка пробелами по 4
|
||||||
const [valueDate, setValueDate] = useState<number | ''>('');
|
const inputVal = e.target.value.replace(/ /g, "");
|
||||||
const [valueCode, setValueCode] = useState<number | ''>('');
|
let inputNumbersOnly = inputVal.replace(/\D/g, "");
|
||||||
|
|
||||||
const formatAndSetCcNumber = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const inputVal = e.target.value.replace(/ /g, ""); //remove all the empty spaces in the input
|
|
||||||
let inputNumbersOnly = inputVal.replace(/\D/g, ""); // Get only digits
|
|
||||||
|
|
||||||
if (inputNumbersOnly.length > 16) {
|
if (inputNumbersOnly.length > 16) {
|
||||||
//If entered value has a length greater than 16 then take only the first 16 digits
|
|
||||||
inputNumbersOnly = inputNumbersOnly.substr(0, 16);
|
inputNumbersOnly = inputNumbersOnly.substr(0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get nd array of 4 digits per an element EX: ["4242", "4242", ...]
|
|
||||||
const splits = inputNumbersOnly.match(/.{1,4}/g);
|
const splits = inputNumbersOnly.match(/.{1,4}/g);
|
||||||
|
|
||||||
let spacedNumber = "";
|
let spacedNumber = "";
|
||||||
if (splits) {
|
if (splits) {
|
||||||
spacedNumber = splits.join(" "); // Join all the splits with an empty space
|
spacedNumber = splits.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
setCcNumber(spacedNumber); // Set the new CC number
|
setCcNumber(spacedNumber);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeDate = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChangeDate = (event: React.ChangeEvent<HTMLInputElement>) => { //valueDate не более 4-х цифр
|
||||||
const inputValue = event.target.value;
|
const inputValue = event.target.value;
|
||||||
if (inputValue.length <= 4) {
|
if (inputValue.length <= 4) {
|
||||||
setValueDate(inputValue === '' ? '' : Number(inputValue));
|
setValueDate(inputValue === '' ? '' : Number(inputValue));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeCode = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChangeCode = (event: React.ChangeEvent<HTMLInputElement>) => { //valueCode не более 3-х цифр
|
||||||
const inputValue = event.target.value;
|
const inputValue = event.target.value;
|
||||||
if (inputValue.length <= 3) {
|
if (inputValue.length <= 3) {
|
||||||
setValueCode(inputValue === '' ? '' : Number(inputValue));
|
setValueCode(inputValue === '' ? '' : Number(inputValue));
|
||||||
@@ -45,7 +41,7 @@ export default function PaymentPage() {
|
|||||||
return(
|
return(
|
||||||
<section className="payment-page">
|
<section className="payment-page">
|
||||||
<h2 className="payment-page__price">
|
<h2 className="payment-page__price">
|
||||||
{price} ₽
|
₽
|
||||||
</h2>
|
</h2>
|
||||||
<div className="payment-page__payment-card">
|
<div className="payment-page__payment-card">
|
||||||
<h3 className="payment-card__heading">
|
<h3 className="payment-card__heading">
|
||||||
|
|||||||
@@ -8,21 +8,29 @@ import ReviewForm from '../components/ReviewForm';
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
export default function ProductPage() {
|
export default function ProductPage() {
|
||||||
function trimText(text: string, limit: number) {
|
function trimText(text: string, limit: number) { //сокращение описания
|
||||||
return text.length > limit ? text.substring(0, limit) + '...' : text;
|
return text.length > limit ? text.substring(0, limit) + '...' : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = useParams();
|
const { id } = useParams(); //возвращает id товара из Url
|
||||||
const [product, setProduct] = useState<Product | null>(null);
|
const [product, setProduct] = useState<Product | null>(null); //состояние для данных о товаре
|
||||||
const [reviews, setReviews] = useState<Reviews[]>([]);
|
const [reviews, setReviews] = useState<Reviews[]>([]); //состаяние для отзывов
|
||||||
const [averageRating, setAverageRating] = useState<number>(0);
|
const [averageRating, setAverageRating] = useState<number>(0); //состояние для средней арифметической оценки товара
|
||||||
const [isDataFetched, setIsDataFetched] = useState(false);
|
const [isDataFetched, setIsDataFetched] = useState(false); //состояние для отслеживания кэширования данных из запроса
|
||||||
|
const totalReviews = reviews.length; //количество отзывов
|
||||||
|
const countReviewsByRate = (rate: number): number => { //подсчёт отзывов с данной оценкой
|
||||||
|
return reviews.filter(review => review.rate === rate).length;
|
||||||
|
};
|
||||||
|
const percentageOfRate = (rate: number): number => { //расчёт процента отзывов с данной оценкой от количетсва всех отзывов
|
||||||
|
const count = countReviewsByRate(rate);
|
||||||
|
return (count / totalReviews) * 100;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => { //запрос к api данных о товаре
|
||||||
axios
|
axios
|
||||||
.get('http://127.0.0.1:8000/api/get/products')
|
.get('http://127.0.0.1:8000/api/get/products')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const productData = response.data.products.find(
|
const productData = response.data.products.find( //"извлечение" данных о товаре из массива по его id
|
||||||
(item: Product) => item.id.toString() === id
|
(item: Product) => item.id.toString() === id
|
||||||
);
|
);
|
||||||
setProduct(productData);
|
setProduct(productData);
|
||||||
@@ -32,15 +40,20 @@ export default function ProductPage() {
|
|||||||
});
|
});
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => { //запрос к api отзывов у товара
|
||||||
if (!isDataFetched) {
|
if (!isDataFetched) { //проверка на кэширование данных
|
||||||
axios
|
axios
|
||||||
.get(`http://127.0.0.1:8000/api/get/reviews/${id}`)
|
.get(`http://127.0.0.1:8000/api/get/reviews/${id}`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
setReviews(response.data.review);
|
setReviews(response.data.review);
|
||||||
const totalRating = response.data.review.reduce((acc: number, review: Reviews) => acc + review.rate, 0);
|
const totalRating = response.data.review.reduce((acc: number, review: Reviews) => acc + review.rate, 0); //общий рейтинг отзывов
|
||||||
const average = totalRating / response.data.review.length;
|
const average = totalRating / response.data.review.length; //средннее арифметический рейтинг всех отзывов
|
||||||
setAverageRating(average);
|
if (response.data.review.length > 0) { //проверка на наличие отзывов
|
||||||
|
setAverageRating(average);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setAverageRating(0);
|
||||||
|
}
|
||||||
setIsDataFetched(true);
|
setIsDataFetched(true);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -89,7 +102,7 @@ export default function ProductPage() {
|
|||||||
{product.description}
|
{product.description}
|
||||||
</p>
|
</p>
|
||||||
<ul className="product-page__tags-ul">
|
<ul className="product-page__tags-ul">
|
||||||
{(product.tags.split('|')).map((tag, index) => (
|
{(product.tags.split('|')).map((tag, index) => ( //разделение тегов
|
||||||
<li key={index} className="product-page__tag-li">
|
<li key={index} className="product-page__tag-li">
|
||||||
{tag}
|
{tag}
|
||||||
</li>
|
</li>
|
||||||
@@ -107,7 +120,7 @@ export default function ProductPage() {
|
|||||||
</span>
|
</span>
|
||||||
<div className="rate-block__star-rating">
|
<div className="rate-block__star-rating">
|
||||||
<div className="star-rating__back-stars">
|
<div className="star-rating__back-stars">
|
||||||
{'★★★★★'.split('').map((star, i) => (
|
{'★★★★★'.split('').map((star, i) => ( //пожалуйста, не спрашивайте как это работает
|
||||||
<span key={`back-star-${i}`}>{star}</span>
|
<span key={`back-star-${i}`}>{star}</span>
|
||||||
))}
|
))}
|
||||||
<div className="star-rating__front-stars" style={{ width: `${(averageRating / 5) * 100}%` }}>
|
<div className="star-rating__front-stars" style={{ width: `${(averageRating / 5) * 100}%` }}>
|
||||||
@@ -119,46 +132,16 @@ export default function ProductPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='rate-block__progressbars-group'>
|
<div className='rate-block__progressbars-group'>
|
||||||
<div className='progressbars-group__progressbar-container'>
|
{[5, 4, 3, 2, 1].map(rate => (
|
||||||
<span className='rate-progressbar__rate-number'>
|
<div className='progressbars-group__progressbar-container' key={rate}>
|
||||||
5
|
<span className='rate-progressbar__rate-number'>
|
||||||
</span>
|
{rate}
|
||||||
<div className='progressbar-container__progressbar'>
|
</span>
|
||||||
<div className='progressbar__active-line'></div>
|
<div className='progressbar-container__progressbar'>
|
||||||
</div>
|
<div className='progressbar__active-line' style={{ width: `${percentageOfRate(rate)}%` }}></div>
|
||||||
</div>
|
</div>
|
||||||
<div className='progressbars-group__progressbar-container'>
|
|
||||||
<span className='rate-progressbar__rate-number'>
|
|
||||||
4
|
|
||||||
</span>
|
|
||||||
<div className='progressbar-container__progressbar'>
|
|
||||||
<div className='progressbar__active-line'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='progressbars-group__progressbar-container'>
|
|
||||||
<span className='rate-progressbar__rate-number'>
|
|
||||||
3
|
|
||||||
</span>
|
|
||||||
<div className='progressbar-container__progressbar'>
|
|
||||||
<div className='progressbar__active-line'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='progressbars-group__progressbar-container'>
|
|
||||||
<span className='rate-progressbar__rate-number'>
|
|
||||||
2
|
|
||||||
</span>
|
|
||||||
<div className='progressbar-container__progressbar'>
|
|
||||||
<div className='progressbar__active-line'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='progressbars-group__progressbar-container'>
|
|
||||||
<span className='rate-progressbar__rate-number'>
|
|
||||||
1
|
|
||||||
</span>
|
|
||||||
<div className='progressbar-container__progressbar'>
|
|
||||||
<div className='progressbar__active-line'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ReviewForm />
|
<ReviewForm />
|
||||||
|
|||||||
@@ -22,4 +22,11 @@ export interface Reviews {
|
|||||||
product_id: number;
|
product_id: number;
|
||||||
rate: number;
|
rate: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeveloperCard {
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
info: string;
|
||||||
|
url: string;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user