Соединение бек и фронт
This commit is contained in:
parent
942c028945
commit
5689c48c01
@ -8,12 +8,13 @@ import io
|
||||
import joblib
|
||||
from flask import Flask, request, jsonify, Blueprint, send_file
|
||||
from flasgger import Swagger
|
||||
from flask_cors import CORS
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
api = Blueprint('api', __name__)
|
||||
Swagger(app)
|
||||
|
||||
CORS(app)
|
||||
# Загружаем модель и scaler
|
||||
model = load_model("my_model_1H.keras")
|
||||
scaler = MinMaxScaler(feature_range=(0, 1))
|
||||
|
@ -16,11 +16,13 @@ function HomePage() {
|
||||
const [marketplaces, setMarketplaces] = useState([]);
|
||||
const [productUrl, setProductUrl] = useState('');
|
||||
const [showPriceHistoryError, setShowPriceHistoryError] = useState(false);
|
||||
const [showResultError, setShowResultError] = useState(false);
|
||||
const [selectedCategory, setSelectedCategory] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMarketplaces = async () => {
|
||||
try {
|
||||
const response = await axios.get('/api/v1/marketplaces');
|
||||
const response = await axios.get('https://mgpj3mxm-8080.euw.devtunnels.ms/api/v1/marketplaces');
|
||||
setMarketplaces(response.data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching marketplaces:', error);
|
||||
@ -34,7 +36,8 @@ function HomePage() {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
if (selectedMarketplace) {
|
||||
const response = await axios.get(`/api/v1/categories?marketplace=${selectedMarketplace}`);
|
||||
console.log(selectedMarketplace)
|
||||
const response = await axios.get(`https://mgpj3mxm-8080.euw.devtunnels.ms/api/v1/categories?marketplace=${selectedMarketplace}`);
|
||||
setCategories(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
@ -46,10 +49,17 @@ function HomePage() {
|
||||
}, [selectedMarketplace]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('Отправлено:', startDate, endDate, selectedMarketplace);
|
||||
navigate('/result', {
|
||||
state: { startDate, endDate, selectedMarketplace }
|
||||
});
|
||||
if (selectedCategory !== "" || selectedMarketplace !== "") {
|
||||
navigate('/result', {
|
||||
state: { startDate, endDate, selectedMarketplace, selectedCategory }
|
||||
});
|
||||
} else {
|
||||
setShowResultError(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCategoryChange = (event) => {
|
||||
setSelectedCategory(event.target.value);
|
||||
};
|
||||
|
||||
const handleProductUrlChange = (event) => {
|
||||
@ -64,7 +74,8 @@ function HomePage() {
|
||||
|
||||
// Проверка существования товара по ссылке
|
||||
try {
|
||||
const response = await fetch(`/api/v1/products/info?productUrl=${productUrl}`);
|
||||
const response = await fetch(`https://mgpj3mxm-8080.euw.devtunnels.ms/api/v1/products/info?productUrl=${productUrl}`);
|
||||
console.log(response.data);
|
||||
if (response.ok) {
|
||||
// Товар найден
|
||||
navigate('/viewProduct', { state: { productUrl } });
|
||||
@ -83,6 +94,7 @@ function HomePage() {
|
||||
setSelectedMarketplace('');
|
||||
} else {
|
||||
setSelectedMarketplace(marketplaceName);
|
||||
setSelectedCategory('');
|
||||
}
|
||||
};
|
||||
|
||||
@ -146,7 +158,7 @@ function HomePage() {
|
||||
height: '90%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography variant="h1" gutterBottom style={{ color: '#132a52', marginBottom: '1.5rem', fontWeight: 'bold' }}>
|
||||
@ -165,36 +177,41 @@ function HomePage() {
|
||||
Выберите маркетплейсы, которые вас интересуют:
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{marketplaces.map((marketplace) => (
|
||||
<Grid item key={marketplace.name}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className="marketplace-button"
|
||||
style={{
|
||||
backgroundColor: selectedMarketplace === marketplace.name ? marketplace.bgColor : '#fcfcf8',
|
||||
color: selectedMarketplace === marketplace.name ? marketplace.textColor : '#16305e',
|
||||
borderRadius: '0.5rem',
|
||||
padding: '1rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxHeight: '150px',
|
||||
alignItems: 'center',
|
||||
minWidth: '400px',
|
||||
}}
|
||||
onClick={() => handleButtonClick(marketplace.name)}
|
||||
>
|
||||
{marketplace.name === 'Wildberries' ? (
|
||||
<img src="https://png.klev.club/uploads/posts/2024-04/png-klev-club-dejs-p-wildberries-logotip-png-16.png" alt="Wildberries" style={{ width: '60px', height: '60px', marginBottom: '0.5rem' }} />
|
||||
) : (
|
||||
<img src="https://pngimg.com/d/ozon_PNG3.png" alt="Ozon" style={{ width: '60px', height: '60px', marginBottom: '0.5rem' }} />
|
||||
)}
|
||||
<Typography variant="h5" gutterBottom style={{ color: selectedMarketplace === marketplace.name ? marketplace.textColor : '#16305e', marginBottom: '0rem' }}>
|
||||
{marketplace.name}
|
||||
</Typography>
|
||||
</Button>
|
||||
</Grid>
|
||||
))}
|
||||
{marketplaces.map((marketplace) => {
|
||||
if (marketplace === 'WILDBERRIES' || marketplace === 'OZON') {
|
||||
return (
|
||||
<Grid item key={marketplace.name}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className="marketplace-button"
|
||||
style={{
|
||||
backgroundColor: selectedMarketplace === marketplace ? marketplace.bgColor : '#fcfcf8',
|
||||
color: selectedMarketplace === marketplace ? marketplace.textColor : '#16305e',
|
||||
borderRadius: '0.5rem',
|
||||
padding: '1rem',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxHeight: '150px',
|
||||
alignItems: 'center',
|
||||
minWidth: '400px',
|
||||
}}
|
||||
onClick={() => handleButtonClick(marketplace)}
|
||||
>
|
||||
{marketplace === 'WILDBERRIES' ? (
|
||||
<img src="https://png.klev.club/uploads/posts/2024-04/png-klev-club-dejs-p-wildberries-logotip-png-16.png" alt="Wildberries" style={{ width: '60px', height: '60px', marginBottom: '0.5rem' }} />
|
||||
) : (
|
||||
<img src="https://pngimg.com/d/ozon_PNG3.png" alt="Ozon" style={{ width: '60px', height: '60px', marginBottom: '0.5rem' }} />
|
||||
)}
|
||||
<Typography variant="h5" gutterBottom style={{ color: selectedMarketplace === marketplace ? marketplace.textColor : '#16305e', marginBottom: '0rem' }}>
|
||||
{marketplace}
|
||||
</Typography>
|
||||
</Button>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
return null; // Для остальных названий, возвращаем null, чтобы не рендерить ничего
|
||||
})}
|
||||
</Grid>
|
||||
<br></br>
|
||||
{/* Комбобокс */}
|
||||
@ -202,15 +219,14 @@ function HomePage() {
|
||||
Выберите категорию:
|
||||
</Typography>
|
||||
<FormControl variant="outlined" style={{ minWidth: '97%' }}>
|
||||
|
||||
<Select
|
||||
labelId="marketplace-select-label"
|
||||
value={selectedMarketplace}
|
||||
onChange={handleMarketplaceChange}
|
||||
value={selectedCategory}
|
||||
onChange={handleCategoryChange}
|
||||
style={{
|
||||
color: '#023247',
|
||||
borderColor: '#4875b2',
|
||||
borderRadius: '0.5rem'// Цвет текста выпадающего списка
|
||||
borderRadius: '0.5rem', // Цвет текста выпадающего списка
|
||||
}}
|
||||
inputProps={{
|
||||
style: {
|
||||
@ -226,50 +242,15 @@ function HomePage() {
|
||||
}}
|
||||
>
|
||||
{categories.map((category) => (
|
||||
<MenuItem key={category.id} value={category.id}>
|
||||
{category.name}
|
||||
<MenuItem key={category} value={category} onChange={handleCategoryChange}>
|
||||
{category}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<br></br>
|
||||
</FormControl>
|
||||
<br></br>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Введите период для сбора данных:
|
||||
</Typography>
|
||||
{startDateError && (
|
||||
<Alert severity="error" style={{ marginBottom: '10px' }}>
|
||||
Дата начала периода не может быть после сегодняшнего дня.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{endDateError && (
|
||||
<Alert severity="error" style={{ marginBottom: '10px' }}>
|
||||
Дата окончания периода не может быть после сегодняшнего дня или раньше даты начала.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="date-pickers" style={{ display: 'flex', alignItems: 'center', marginTop: '0.5rem' }}>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
onChange={handleStartDateChange}
|
||||
className="datePickerInput"
|
||||
wrapperClassName="datePicker"
|
||||
dateFormat="dd.MM.yyyy"
|
||||
popperPlacement="bottom"
|
||||
showMonthDropdown
|
||||
/>
|
||||
<span className="date-separator" style={{ margin: '0 0.5rem', color: '#2a8e9e', fontSize: '30px' }}> - </span>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={handleEndDateChange}
|
||||
className="datePickerInput"
|
||||
wrapperClassName="datePicker"
|
||||
dateFormat="dd.MM.yyyy"
|
||||
popperPlacement="bottom"
|
||||
showMonthDropdown
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@ -280,13 +261,18 @@ function HomePage() {
|
||||
padding: '0.75rem 1.5rem',
|
||||
backgroundColor: '#4875b2',
|
||||
marginTop: '1.5rem',
|
||||
minWidth: '97%'
|
||||
minWidth: '97%', // Можно оставить или изменить на '100%'
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" gutterBottom style={{ color: '#fcfcfb', marginBottom: '0rem' }}>
|
||||
Получить рекомендации
|
||||
</Typography>
|
||||
</Button>
|
||||
{showResultError && (
|
||||
<Alert severity="error" style={{ width: '94%', marginTop: '1rem' }}>
|
||||
Выберите маркетплейс и категорию
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
@ -306,8 +292,11 @@ function HomePage() {
|
||||
<div style={{
|
||||
position: 'relative',
|
||||
width: '97%',
|
||||
height: '700px',
|
||||
overflow: 'hidden'
|
||||
height: 'auto', // Изменение на auto для высоты
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<img
|
||||
src="https://cdn.prod.website-files.com/61ebe5f773be1acd620f8208/61fb879dfccdca6a20c66d4a_e-commerce-marketplace.gif"
|
||||
@ -315,31 +304,29 @@ function HomePage() {
|
||||
className="main-image"
|
||||
style={{
|
||||
borderRadius: '0.5rem',
|
||||
maxWidth: '80%',
|
||||
maxWidth: '66%',
|
||||
height: 'auto',
|
||||
position: 'absolute',
|
||||
top: '80%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -100%)'
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h4" style={{
|
||||
color: '#132a52',
|
||||
position: 'absolute',
|
||||
top: '60%',
|
||||
width: '80%',
|
||||
}}>
|
||||
Вы можете посмотреть историю изменения цены конкретного товара при помощи его URL.
|
||||
</Typography>
|
||||
<Typography variant="h4" style={{
|
||||
color: '#132a52',
|
||||
position: 'absolute',
|
||||
top: '80%',
|
||||
width: '80%'
|
||||
}}>
|
||||
Введите ссылку на товар:
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<Typography variant="h4" style={{
|
||||
color: '#132a52',
|
||||
width: '80%',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
Вы можете посмотреть историю изменения цены конкретного товара при помощи его URL.
|
||||
</Typography>
|
||||
|
||||
<Typography variant="h4" style={{
|
||||
color: '#132a52',
|
||||
width: '80%',
|
||||
textAlign: 'center',
|
||||
marginTop: '1rem',
|
||||
}}>
|
||||
Введите ссылку на товар:
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
label="Ссылка на товар"
|
||||
placeholder="https://www.ozon.ru/..."
|
||||
@ -348,32 +335,37 @@ function HomePage() {
|
||||
value={productUrl}
|
||||
onChange={handleProductUrlChange}
|
||||
style={{
|
||||
marginTop: '-3.5rem',
|
||||
color: '#132a52',
|
||||
borderColor: '#4875b2',
|
||||
borderRadius: '1rem',
|
||||
maxWidth: '97%'
|
||||
marginTop: '1rem', // Теперь пространство между текстом и полем ввода
|
||||
maxWidth: '97%',
|
||||
}}
|
||||
/>
|
||||
<Button variant="contained" color="primary" onClick={handleViewPriceHistory}
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleViewPriceHistory}
|
||||
style={{
|
||||
borderRadius: '0.5rem',
|
||||
padding: '0.75rem 1.5rem',
|
||||
backgroundColor: '#4875b2',
|
||||
marginTop: '1.5rem',
|
||||
minWidth: '97%'
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" gutterBottom style={{ color: '#fcfcfb', marginBottom: '0rem' }}>
|
||||
Посмотреть историю цены
|
||||
</Typography>
|
||||
</Button>
|
||||
|
||||
{showPriceHistoryError && (
|
||||
<Alert severity="error" style={{ marginTop: '1rem' }}>
|
||||
<Alert severity="error" style={{ width: '94%', marginTop: '1rem', textAlign: 'center' }}>
|
||||
Неверный URL товара или товар не найден.
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
|
@ -1,28 +1,49 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { Typography, Box, Grid, Container, Card, CardContent, CardHeader, Button } from '@mui/material';
|
||||
import { Typography, Box, Grid, Container, Card, CardContent, CardHeader, Button, CircularProgress } from '@mui/material';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';
|
||||
|
||||
const Result = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { startDate, endDate, selectedMarketplace } = location.state || {};
|
||||
|
||||
const generateDates = (startDate, endDate) => {
|
||||
const dates = [];
|
||||
let currentDate = new Date(startDate);
|
||||
while (currentDate <= endDate) {
|
||||
dates.push(new Date(currentDate));
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
}
|
||||
return dates;
|
||||
};
|
||||
|
||||
const data = generateDates(startDate, endDate).map((date, index) => ({
|
||||
date: date,
|
||||
price: Math.floor(Math.random() * 200) + 50
|
||||
}));
|
||||
const { startDate, endDate, selectedMarketplace, selectedCategory } = location.state || {};
|
||||
const [predictData, setPredictData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [imageUrl, setImageUrl] = useState('');
|
||||
useEffect(() => {
|
||||
const fetchPredictPrice = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:5000/api/predict_price');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setPredictData(data);
|
||||
} else {
|
||||
console.error("Ошибка запроса к API");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Ошибка при получении данных:", error);
|
||||
}
|
||||
};
|
||||
const fetchChart = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:5000/api/plot');
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
setImageUrl(url);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при загрузке графика:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
fetchChart();
|
||||
fetchPredictPrice();
|
||||
}, []);
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
@ -72,8 +93,8 @@ const Result = () => {
|
||||
Выбранный маркетплейс: {selectedMarketplace}
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom style={{ color: '#023247' }}>
|
||||
Период: {startDate?.toLocaleDateString()} по {endDate?.toLocaleDateString()}
|
||||
</Typography>
|
||||
Выбранная категория: {selectedCategory}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
@ -81,31 +102,26 @@ const Result = () => {
|
||||
<Grid item xs={12}>
|
||||
<Card style={{ backgroundColor: '#fcfcf8', borderRadius: '1rem' }}>
|
||||
<CardHeader title="Анализ" style={{ color: '#023247' }} />
|
||||
<CardContent>
|
||||
<LineChart width={600} height={300} data={data}>
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tickFormatter={(unixTime) => new Date(unixTime).toLocaleDateString()}
|
||||
tickMargin={10}
|
||||
stroke="#023247"
|
||||
/>
|
||||
<YAxis stroke="#023247" />
|
||||
<CartesianGrid stroke="#f5f5f5" />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="price" stroke="#2a8e9e" activeDot={{ r: 8 }} />
|
||||
</LineChart>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<CircularProgress />
|
||||
) : (
|
||||
<img src={imageUrl} alt="График предсказанных и фактических цен" style={{ width: '100%', borderRadius: '1rem' }} />
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Card style={{ backgroundColor: '#fcfcf8', borderRadius: '1rem' }}>
|
||||
<CardHeader title="Рекомендации" style={{ color: '#023247' }} />
|
||||
|
||||
<CardContent>
|
||||
<Typography variant="body1" gutterBottom style={{ color: '#023247' }}>
|
||||
Здесь будут отображаться рекомендации, основанные на анализе данных.
|
||||
{predictData ? (
|
||||
`Продукт лучше покупать в ${predictData.min_price_day.date}. Предсказанная цена: ${predictData.min_price_day.price} руб.`
|
||||
) : (
|
||||
"Загрузка данных..."
|
||||
)}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
@ -31,16 +31,24 @@ const ViewProduct = () => {
|
||||
|
||||
const sign = zoneOffset >= 0 ? '+' : '-';
|
||||
|
||||
// Исправлено: использование шаблонных строк для форматирования часового пояса
|
||||
const formattedZoneOffset = `${sign}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
||||
|
||||
const { productUrl, from, to } = location.state || {};
|
||||
// Определение 'to' как текущая дата
|
||||
const to = date.toISOString().split('T')[0]; // Текущая дата в формате YYYY-MM-DD
|
||||
const from = new Date(date);
|
||||
from.setDate(date.getDate() - 30); // Дата минус 30 дней
|
||||
const formattedFrom = from.toISOString().split('T')[0]; // Дата в формате YYYY-MM-DD
|
||||
|
||||
// Извлечение productUrl из состояния location
|
||||
const { productUrl } = location.state || {};
|
||||
const [productData, setProductData] = useState(null);
|
||||
const [chartData, setChartData] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProductData = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/products/info?productUrl=${productUrl}`);
|
||||
const response = await fetch(`https://mgpj3mxm-8080.euw.devtunnels.ms/api/v1/products/info?productUrl=${productUrl}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setProductData(data);
|
||||
@ -54,14 +62,18 @@ const ViewProduct = () => {
|
||||
|
||||
const fetchPriceHistory = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/v1/products/price-history?productUrl=${productUrl}&from=${from}&to=${to}&zoneOffset=${formattedZoneOffset}`);
|
||||
const response = await fetch(`https://mgpj3mxm-8080.euw.devtunnels.ms/api/v1/products/price-history?productUrl=${productUrl}&from=${formattedFrom}&to=${to}&zoneOffset=${formattedZoneOffset}`);
|
||||
if (response.ok) {
|
||||
const priceHistoryData = await response.json();
|
||||
// Преобразование данных в нужный формат
|
||||
const priceHistory = Object.entries(priceHistoryData.priceHistory).map(([date, price]) => ({
|
||||
date: new Date(date),
|
||||
date: new Date(date), // Преобразование строки даты в объект Date
|
||||
price: price,
|
||||
}));
|
||||
setChartData(priceHistory);
|
||||
|
||||
// Сортировка по дате
|
||||
const sortedPriceHistory = priceHistory.sort((a, b) => a.date - b.date);
|
||||
setChartData(sortedPriceHistory);
|
||||
} else {
|
||||
console.error("Ошибка запроса к API");
|
||||
}
|
||||
@ -70,11 +82,10 @@ const ViewProduct = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (productUrl) {
|
||||
fetchProductData();
|
||||
fetchPriceHistory();
|
||||
}
|
||||
}, [productUrl, from, to, formattedZoneOffset]);
|
||||
fetchProductData();
|
||||
fetchPriceHistory();
|
||||
}, [productUrl, formattedFrom, to, formattedZoneOffset]);
|
||||
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
@ -127,13 +138,10 @@ const ViewProduct = () => {
|
||||
{productData.marketplaceName}
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom style={{ color: theme.palette.secondary.main }}>
|
||||
<a href={productData.link} target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.primary.main, textDecoration: 'underline' }}>
|
||||
Ссылка на товар
|
||||
<a href={productUrl} target="_blank" rel="noopener noreferrer" style={{ color: theme.palette.primary.main, textDecoration: 'underline' }}>
|
||||
Перейти на страницу товара
|
||||
</a>
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom style={{ color: theme.palette.secondary.main, fontWeight: 'bold' }}>
|
||||
{productData.brand}
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom style={{ color: theme.palette.secondary.main, fontWeight: 'bold' }}>
|
||||
{productData.productName}
|
||||
</Typography>
|
||||
@ -165,6 +173,7 @@ const ViewProduct = () => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
)}
|
||||
</Container>
|
||||
|
Loading…
Reference in New Issue
Block a user