.
This commit is contained in:
36
src/App.css
36
src/App.css
@@ -1,11 +1,45 @@
|
|||||||
|
/* Основные стили приложения */
|
||||||
.App {
|
.App {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
background: #fff; /* Добавляем белый фон */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Стили для главного контента */
|
||||||
main {
|
main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-bottom: 40px;
|
padding: 20px 0 40px;
|
||||||
|
max-width: 1200px; /* Ограничиваем ширину контента */
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto; /* Центрируем контент */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Плавная прокрутка */
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для секций */
|
||||||
|
section {
|
||||||
|
padding: 60px 0;
|
||||||
|
scroll-margin-top: 80px; /* Учитываем высоту шапки */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Отступы между секциями */
|
||||||
|
section:not(:last-child) {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Для адаптивности */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
main {
|
||||||
|
padding: 15px 20px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
padding: 40px 0;
|
||||||
|
scroll-margin-top: 70px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
14
src/App.js
14
src/App.js
@@ -14,24 +14,34 @@ function App() {
|
|||||||
<Router>
|
<Router>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={
|
<Route path="/" element={
|
||||||
<>
|
<>
|
||||||
<h1 style={{ textAlign: 'center', marginTop: '20px' }}>
|
<h1 className="main-title">
|
||||||
Добро пожаловать в SkinCare Advisor!
|
Добро пожаловать в SkinCare Advisor!
|
||||||
</h1>
|
</h1>
|
||||||
<section style={{ maxWidth: '700px', margin: '40px auto', padding: '0 20px' }}>
|
|
||||||
|
<section id="quiz" className="section-container">
|
||||||
<SkinTypeQuiz />
|
<SkinTypeQuiz />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="products" className="section-container">
|
||||||
<PopularProducts />
|
<PopularProducts />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="reviews" className="section-container">
|
||||||
<Reviews />
|
<Reviews />
|
||||||
|
</section>
|
||||||
</>
|
</>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<Route path="/register" element={<Register />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -75,7 +75,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Адаптивность */
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.footer-content {
|
.footer-content {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -93,16 +92,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Фиксированный футер (опционально) */
|
|
||||||
/*
|
|
||||||
.App {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-footer {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ function Footer() {
|
|||||||
|
|
||||||
<div className="copyright">
|
<div className="copyright">
|
||||||
© {new Date().getFullYear()} SkinCare Advisor. Все права защищены.
|
© {new Date().getFullYear()} SkinCare Advisor. Все права защищены.
|
||||||
</div>
|
</div> {/*JS выражение, которое подставляет текущий год*/}
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,111 +1,141 @@
|
|||||||
/* Основные стили шапки */
|
.site-header { /* Основной контейнер шапки сайта */
|
||||||
.site-header {
|
|
||||||
background: white;
|
background: white;
|
||||||
padding: 15px 0;
|
padding: 15px 0; /* Отступы сверху/снизу */
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0; /* Привязка к верху */
|
||||||
z-index: 1000;
|
z-index: 1000; /* Поверх других элементов */
|
||||||
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 15px rgba(0,0,0,0.07); /* Тень снизу */
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0; /* Граница снизу */
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-content {
|
.header-content { /* Контейнер содержимого */
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto; /* Центрирование */
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center; /* Вертикальное выравнивание */
|
||||||
padding: 0 20px;
|
padding: 0 20px; /* Отступы по бокам */
|
||||||
gap: 30px;
|
gap: 22px; /* Расстояние между элементами */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стили для логотипа */
|
.logo-link { /* Ссылка с логотипом */
|
||||||
.logo-link {
|
|
||||||
text-decoration: none;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center; /* Выравнивание по центру */
|
||||||
transition: transform 0.3s ease;
|
text-decoration: none; /* Без подчеркивания */
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-link:hover {
|
.navigation ul { /* Навигационное меню */
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Стили для поиска */
|
|
||||||
.search-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
gap: 18px; /* Расстояние между пунктами */
|
||||||
max-width: 600px;
|
list-style: none; /* Убрать маркеры */
|
||||||
gap: 12px;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.navigation a { /* Ссылки меню */
|
||||||
flex: 1;
|
|
||||||
padding: 12px 25px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 30px;
|
|
||||||
background: white;
|
|
||||||
color: #333;
|
color: #333;
|
||||||
|
text-decoration: none; /* Без подчеркивания */
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
transition: all 0.3s;
|
font-weight: 500; /* Насыщенность */
|
||||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
padding: 8px 14px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.2s, color 0.2s; /* Анимация */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input:focus {
|
.navigation a:hover { /* При наведении */
|
||||||
border-color: #6C63FF;
|
background: #f3f2ff;
|
||||||
box-shadow: 0 0 0 3px rgba(108, 99, 255, 0.1);
|
color: #6C63FF;
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input::placeholder {
|
.search-container { /* Поисковая строка */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 260px;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 30px;
|
||||||
|
background: #f6f6fa;
|
||||||
|
border-radius: 22px;
|
||||||
|
box-shadow: 0 1px 4px rgba(108,99,255,0.05); /* Тень */
|
||||||
|
padding: 2px 4px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input { /* Поле ввода */
|
||||||
|
flex: 1; /* Заполнение пространства */
|
||||||
|
padding: 8px 14px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: transparent; /* Прозрачный фон */
|
||||||
|
color: #333;
|
||||||
|
font-size: 15px;
|
||||||
|
outline: none; /* Без обводки */
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input::placeholder { /* Плейсхолдер */
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn {
|
.search-btn { /* Кнопка поиска */
|
||||||
padding: 0 30px;
|
padding: 7px 18px;
|
||||||
background: #ffffff;
|
background: #fff;
|
||||||
color: rgb(0, 0, 0);
|
color: #6C63FF;
|
||||||
border: none;
|
border: 1px solid #6C63FF;
|
||||||
border-radius: 30px;
|
border-radius: 18px;
|
||||||
cursor: pointer;
|
font-size: 15px;
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: all 0.3s;
|
cursor: pointer; /* Курсор-указатель */
|
||||||
box-shadow: 0 2px 10px rgba(108, 99, 255, 0.2);
|
transition: background 0.2s, color 0.2s; /* Анимация */
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn:hover {
|
.search-btn:hover { /* При наведении */
|
||||||
background: #e2e2e2;
|
background: #6C63FF;
|
||||||
transform: translateY(-1px);
|
color: #fff;
|
||||||
box-shadow: 0 4px 12px rgba(108, 99, 255, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стили для кнопок авторизации */
|
.auth-buttons { /* Кнопки авторизации */
|
||||||
.auth-buttons {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15px;
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-link {
|
.auth-link { /* Ссылка авторизации */
|
||||||
color: #555;
|
color: #555;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 10px 20px;
|
padding: 8px 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
transition: all 0.3s;
|
transition: background 0.2s, color 0.2s; /* Анимация */
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-link:hover {
|
.auth-link:hover { /* При наведении */
|
||||||
color: #6C63FF;
|
color: #6C63FF;
|
||||||
background: rgba(108, 99, 255, 0.05);
|
background: rgba(108, 99, 255, 0.07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-link.register {
|
.auth-link.register { /* Кнопка регистрации */
|
||||||
background: #6C63FF;
|
background: #6C63FF;
|
||||||
color: white;
|
color: white;
|
||||||
box-shadow: 0 2px 10px rgba(108, 99, 255, 0.2);
|
box-shadow: 0 2px 10px rgba(108, 99, 255, 0.08); /* Тень */
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-link.register:hover {
|
.auth-link.register:hover { /* При наведении */
|
||||||
background: #5a52d4;
|
background: #5a52d4;
|
||||||
box-shadow: 0 4px 12px rgba(108, 99, 255, 0.3);
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) { /* Адаптивность */
|
||||||
|
.header-content {
|
||||||
|
flex-wrap: wrap; /* Перенос строк */
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.navigation, .search-container, .auth-buttons {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.navigation ul { /* Меню */
|
||||||
|
flex-wrap: wrap; /* Перенос пунктов */
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.search-container { /* Поиск */
|
||||||
|
max-width: 100%; /* Полная ширина */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Logo from './Logo'; // Убедитесь в правильности пути
|
import Logo from './Logo';
|
||||||
|
import Navigation from './Navigation';
|
||||||
import './Header.css';
|
import './Header.css';
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
@@ -11,10 +12,12 @@ function Header() {
|
|||||||
<Logo />
|
<Logo />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Navigation />
|
||||||
|
|
||||||
<div className="search-container">
|
<div className="search-container">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Поиск продуктов..."
|
placeholder="Поиск..."
|
||||||
className="search-input"
|
className="search-input"
|
||||||
/>
|
/>
|
||||||
<button className="search-btn">Поиск</button>
|
<button className="search-btn">Поиск</button>
|
||||||
|
|||||||
@@ -1,53 +1,22 @@
|
|||||||
.logo {
|
.logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 12px;
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-image {
|
.logo-image {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: auto;
|
width: auto;
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-image:hover {
|
|
||||||
transform: scale(1.05) rotate(-5deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-text {
|
.logo-text {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
color: #333; /* Изменили на тёмный цвет */
|
color: #333;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px; /* Доб 0.5px между символами */
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-text::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
bottom: -5px;
|
|
||||||
left: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 2px;
|
|
||||||
background: #6C63FF;
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo:hover .logo-text::after {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-text span {
|
.logo-text span {
|
||||||
color: #6C63FF;
|
color: #6C63FF;
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo:hover .logo-text span {
|
|
||||||
color: #5a52d4;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ function Navigation() {
|
|||||||
return (
|
return (
|
||||||
<nav className="navigation">
|
<nav className="navigation">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">Помощь</a></li>
|
<li>
|
||||||
<li><a href="/contacts">Контакты</a></li>
|
<a href="#products">Товары</a>
|
||||||
<li><a href="/skin-types">Типы кожи</a></li>
|
</li>
|
||||||
<li><a href="/reviews">Отзывы</a></li>
|
<li>
|
||||||
<li><a href="/auth">Вход/Регистрация</a></li>
|
<a href="#reviews">Отзывы</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#quiz">Опросник</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
.popular-products {
|
.popular-products { /* Контейнер секции */
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 40px auto;
|
margin: 40px auto; /* Центрирование */
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popular-products h2 {
|
.popular-products h2 { /* Заголовок */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.products-grid {
|
.products-grid { /* Сетка товаров */
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); /* Автоподбор колонок */
|
||||||
gap: 30px;
|
gap: 30px; /* Расстояние между карточками */
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card {
|
.product-card { /* Карточка товара */
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden; /* Скрытие выходящего контента */
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s; /* Анимация */
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover {
|
.product-card:hover { /* Эффект при наведении */
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px); /* Сдвиг вверх */
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image {
|
.product-image { /* Изображение товара */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
object-fit: cover;
|
object-fit: cover; /* Обрезка изображения */
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card h3 {
|
.product-card h3 { /* Название товара */
|
||||||
padding: 15px 15px 0;
|
padding: 15px 15px 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card p {
|
.product-card p { /* Описание товара */
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-footer {
|
.product-footer { /* Подвал карточки */
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between; /* Распределение пространства */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,20 +58,19 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-btn {
|
.details-btn { /* Кнопка "Подробнее" */
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer; /* Курсор-указатель */
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
opacity: 0; /* Скрываем кнопку по умолчанию */
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-card:hover .details-btn {
|
.product-card:hover .details-btn { /* Кнопка при наведении */
|
||||||
background: #6C63FF;
|
background: #6C63FF;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #6C63FF;
|
border-color: #6C63FF;
|
||||||
opacity: 1; /* Показываем кнопку при наведении */
|
opacity: 1; /* Показываем кнопку */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,8 @@ import product1 from './img/product1.jpg';
|
|||||||
import product2 from './img/product2.jpg';
|
import product2 from './img/product2.jpg';
|
||||||
import product3 from './img/product3.jpg';
|
import product3 from './img/product3.jpg';
|
||||||
import product4 from './img/product4.jpg';
|
import product4 from './img/product4.jpg';
|
||||||
|
import product5 from './img/product5.png';
|
||||||
|
import product6 from './img/product6.png';
|
||||||
|
|
||||||
const products = [
|
const products = [
|
||||||
{
|
{
|
||||||
@@ -33,15 +35,31 @@ const products = [
|
|||||||
image: product4,
|
image: product4,
|
||||||
description: "Восстановление во время сна",
|
description: "Восстановление во время сна",
|
||||||
price: "1 950 ₽"
|
price: "1 950 ₽"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Солнцезащитный крем SPF 50",
|
||||||
|
image: product5,
|
||||||
|
description: "Защита от UVA/UVB лучей",
|
||||||
|
price: "1 250 ₽"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "Пилинг для лица",
|
||||||
|
image: product6,
|
||||||
|
description: "Мягкое отшелушивание кожи",
|
||||||
|
price: "1 780 ₽"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
function PopularProducts() {
|
function PopularProducts() { /* берет массив products, преобразует каждый товар
|
||||||
|
в HTML-карточку и выводит их в адаптивную сетку.*/
|
||||||
return (
|
return (
|
||||||
<section className="popular-products">
|
<section className="popular-products"> {/* Основная секция */}
|
||||||
<h2>Популярные косметические средства</h2>
|
<h2>Популярные косметические средства</h2>
|
||||||
<div className="products-grid">
|
|
||||||
{products.map(product => (
|
<div className="products-grid"> {/* Контейнер сетки */}
|
||||||
|
{products.map(product => ( /* Перебираем массив товаров */
|
||||||
<div key={product.id} className="product-card">
|
<div key={product.id} className="product-card">
|
||||||
<img
|
<img
|
||||||
src={product.image}
|
src={product.image}
|
||||||
|
|||||||
BIN
src/components/PopularProducts/img/product5.png
Normal file
BIN
src/components/PopularProducts/img/product5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
BIN
src/components/PopularProducts/img/product6.png
Normal file
BIN
src/components/PopularProducts/img/product6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
@@ -30,7 +30,7 @@
|
|||||||
color: #222;
|
color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-meta {
|
.review-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@@ -45,4 +45,3 @@
|
|||||||
color: #FFD700;
|
color: #FFD700;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './Reviews.css';
|
import './Reviews.css';
|
||||||
|
|
||||||
|
// Массив с отзывами пользователей
|
||||||
const reviews = [
|
const reviews = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -22,6 +23,7 @@ const reviews = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Компонент для отображения звездного рейтинга
|
||||||
function StarRating({ rating }) {
|
function StarRating({ rating }) {
|
||||||
return (
|
return (
|
||||||
<span className="star-rating">
|
<span className="star-rating">
|
||||||
@@ -31,15 +33,18 @@ function StarRating({ rating }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Основной компонент отзывов
|
||||||
function Reviews() {
|
function Reviews() {
|
||||||
return (
|
return (
|
||||||
<section className="reviews-section">
|
<section className="reviews-section">
|
||||||
<h2>Отзывы пользователей</h2>
|
<h2>Отзывы пользователей</h2>
|
||||||
<div className="reviews-list">
|
|
||||||
|
<div className="reviews-list">{/* Перебираем массив отзывов */}
|
||||||
{reviews.map(review => (
|
{reviews.map(review => (
|
||||||
|
// Карточка отзыва с уникальным ключом
|
||||||
<div key={review.id} className="review-card">
|
<div key={review.id} className="review-card">
|
||||||
<p className="review-text">"{review.text}"</p>
|
<p className="review-text">"{review.text}"</p>
|
||||||
<div className="review-meta">
|
<div className="review-info">
|
||||||
<span className="review-author">{review.author}</span>
|
<span className="review-author">{review.author}</span>
|
||||||
<StarRating rating={review.rating} />
|
<StarRating rating={review.rating} />
|
||||||
</div>
|
</div>
|
||||||
@@ -50,4 +55,5 @@ function Reviews() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default Reviews;
|
export default Reviews;
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
function Question({ question, answer, onAnswer, isLast, onNext }) {
|
function Question({ question, answer, onAnswer, isLast, onNext }) {
|
||||||
|
// Выбранные ответы
|
||||||
const [selected, setSelected] = useState(() => {
|
const [selected, setSelected] = useState(() => {
|
||||||
return question.type === 'multiple'
|
return question.type === 'multiple'
|
||||||
? Array.isArray(answer) ? [...answer] : []
|
? Array.isArray(answer) ? [...answer] : []
|
||||||
: answer || '';
|
: answer || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Текст для поля "Другое"
|
||||||
const [otherText, setOtherText] = useState('');
|
const [otherText, setOtherText] = useState('');
|
||||||
|
|
||||||
// Инициализация otherText
|
// Инициализация текста для "Другое"
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (question.type === 'multiple' && Array.isArray(answer)) {
|
if (question.type === 'multiple' && Array.isArray(answer)) {
|
||||||
const otherItem = answer.find(item => item?.other);
|
const otherItem = answer.find(item => item?.other);
|
||||||
@@ -17,7 +19,7 @@ function Question({ question, answer, onAnswer, isLast, onNext }) {
|
|||||||
}
|
}
|
||||||
}, [answer, question.type]);
|
}, [answer, question.type]);
|
||||||
|
|
||||||
// Синхронизация с пропсом answer
|
// Синхронизация с внешними изменениями
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (question.type === 'multiple') {
|
if (question.type === 'multiple') {
|
||||||
setSelected(Array.isArray(answer) ? answer : []);
|
setSelected(Array.isArray(answer) ? answer : []);
|
||||||
@@ -26,6 +28,7 @@ function Question({ question, answer, onAnswer, isLast, onNext }) {
|
|||||||
}
|
}
|
||||||
}, [answer, question.type]);
|
}, [answer, question.type]);
|
||||||
|
|
||||||
|
// Обработчик изменения выбора
|
||||||
const handleChange = (option, isOther = false) => {
|
const handleChange = (option, isOther = false) => {
|
||||||
if (question.type === 'single') {
|
if (question.type === 'single') {
|
||||||
setSelected(option);
|
setSelected(option);
|
||||||
@@ -35,6 +38,7 @@ function Question({ question, answer, onAnswer, isLast, onNext }) {
|
|||||||
|
|
||||||
let newSelected = Array.isArray(selected) ? [...selected] : [];
|
let newSelected = Array.isArray(selected) ? [...selected] : [];
|
||||||
|
|
||||||
|
// Логика для чекбокса "Другое"
|
||||||
if (isOther) {
|
if (isOther) {
|
||||||
const existingOther = newSelected.findIndex(item => item?.other);
|
const existingOther = newSelected.findIndex(item => item?.other);
|
||||||
if (existingOther >= 0) {
|
if (existingOther >= 0) {
|
||||||
@@ -56,6 +60,7 @@ function Question({ question, answer, onAnswer, isLast, onNext }) {
|
|||||||
onAnswer(question.id, newSelected);
|
onAnswer(question.id, newSelected);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Обработчик изменения текста для "Другое"
|
||||||
const handleOtherTextChange = (e) => {
|
const handleOtherTextChange = (e) => {
|
||||||
const text = e.target.value;
|
const text = e.target.value;
|
||||||
setOtherText(text);
|
setOtherText(text);
|
||||||
@@ -66,11 +71,13 @@ function Question({ question, answer, onAnswer, isLast, onNext }) {
|
|||||||
onAnswer(question.id, newSelected);
|
onAnswer(question.id, newSelected);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Отправка формы
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onNext();
|
onNext();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Нормализация выбранных значений
|
||||||
const safeSelected = question.type === 'multiple'
|
const safeSelected = question.type === 'multiple'
|
||||||
? (Array.isArray(selected) ? selected : [])
|
? (Array.isArray(selected) ? selected : [])
|
||||||
: selected;
|
: selected;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Question from './Question';
|
|||||||
import Result from './Result';
|
import Result from './Result';
|
||||||
import './SkinTypeQuiz.css';
|
import './SkinTypeQuiz.css';
|
||||||
|
|
||||||
|
// Список всех вопросов опросника
|
||||||
const questionsMock = [
|
const questionsMock = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -49,15 +50,25 @@ const questionsMock = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function SkinTypeQuiz() {
|
function SkinTypeQuiz() {
|
||||||
|
// Текущий индекс вопроса
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
|
// Все ответы пользователя
|
||||||
const [answers, setAnswers] = useState({});
|
const [answers, setAnswers] = useState({});
|
||||||
|
|
||||||
|
// Флаг показа результатов
|
||||||
const [showResult, setShowResult] = useState(false);
|
const [showResult, setShowResult] = useState(false);
|
||||||
|
|
||||||
|
// Рекомендации по уходу
|
||||||
const [recommendations, setRecommendations] = useState('');
|
const [recommendations, setRecommendations] = useState('');
|
||||||
|
|
||||||
const questions = questionsMock;
|
const questions = questionsMock;
|
||||||
|
|
||||||
|
// Обработчик выбора ответа
|
||||||
const handleAnswer = (questionId, answer) => {
|
const handleAnswer = (questionId, answer) => {
|
||||||
const currentQuestion = questions.find(q => q.id === questionId);
|
const currentQuestion = questions.find(q => q.id === questionId);
|
||||||
|
|
||||||
|
// Для чекбоксов: преобразуем в массив если необходимо
|
||||||
const safeAnswer = currentQuestion.type === 'multiple'
|
const safeAnswer = currentQuestion.type === 'multiple'
|
||||||
? Array.isArray(answer) ? answer : []
|
? Array.isArray(answer) ? answer : []
|
||||||
: answer;
|
: answer;
|
||||||
@@ -65,6 +76,7 @@ function SkinTypeQuiz() {
|
|||||||
setAnswers(prev => ({ ...prev, [questionId]: safeAnswer }));
|
setAnswers(prev => ({ ...prev, [questionId]: safeAnswer }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Переход к следующему вопросу или показ результатов
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
if (currentIndex + 1 < questions.length) {
|
if (currentIndex + 1 < questions.length) {
|
||||||
setCurrentIndex(currentIndex + 1);
|
setCurrentIndex(currentIndex + 1);
|
||||||
@@ -75,10 +87,12 @@ function SkinTypeQuiz() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Показываем результаты если опрос завершен
|
||||||
if (showResult) {
|
if (showResult) {
|
||||||
return <Result recommendations={recommendations} />;
|
return <Result recommendations={recommendations} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Рендер текущего вопроса
|
||||||
return (
|
return (
|
||||||
<Question
|
<Question
|
||||||
question={questions[currentIndex]}
|
question={questions[currentIndex]}
|
||||||
@@ -90,8 +104,10 @@ function SkinTypeQuiz() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Генерация рекомендаций на основе ответов
|
||||||
function calculateRecommendations(answers) {
|
function calculateRecommendations(answers) {
|
||||||
const question3Answer = answers[3]; // ID=3 — вопрос про тип кожи
|
const question3Answer = answers[3]; // Ответ на вопрос о типе кожи
|
||||||
|
|
||||||
if (question3Answer === 'Сухая и тянущаяся') {
|
if (question3Answer === 'Сухая и тянущаяся') {
|
||||||
return 'Рекомендуется использовать увлажняющие средства и средства для чувствительной кожи.';
|
return 'Рекомендуется использовать увлажняющие средства и средства для чувствительной кожи.';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,42 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react'; // Хук состояния
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import './AuthForms.css';
|
import './AuthForms.css';
|
||||||
|
|
||||||
function Login() {
|
function Login() {
|
||||||
|
// Состояние для хранения данных формы
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Хук для программной навигации
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Обработчик изменений в полях ввода
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev, // Сохранение предыдущих значений
|
||||||
[name]: value
|
[name]: value // Динамическое обновление поля
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Обработчик отправки формы
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault(); // Отмена стандартного поведения
|
||||||
console.log('Вход:', formData);
|
console.log('Вход:', formData); // Логирование данных (в реальном приложении - запрос к API)
|
||||||
navigate('/');
|
navigate('/'); // Перенаправление на главную страницу
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// Основной контейнер страницы
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<div className="auth-container">
|
<div className="auth-container">
|
||||||
<h2>Вход</h2>
|
<h2>Вход</h2>
|
||||||
|
|
||||||
|
{/* Форма с обработчиком отправки */}
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
|
{/* Группа полей для email */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Email</label>
|
<label>Email</label>
|
||||||
<input
|
<input
|
||||||
@@ -36,10 +44,11 @@ function Login() {
|
|||||||
name="email"
|
name="email"
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required // Обязательное поле
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Группа полей для пароля */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Пароль</label>
|
<label>Пароль</label>
|
||||||
<input
|
<input
|
||||||
@@ -51,11 +60,13 @@ function Login() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопка отправки формы */}
|
||||||
<button type="submit" className="auth-btn">
|
<button type="submit" className="auth-btn">
|
||||||
Войти
|
Войти
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{/* Ссылка для перехода к регистрации */}
|
||||||
<div className="auth-switch">
|
<div className="auth-switch">
|
||||||
<span>Нет аккаунта? <a href="/register">Создать</a></span>
|
<span>Нет аккаунта? <a href="/register">Создать</a></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,27 +1,35 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react'; //для управления состоянием формы
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import './AuthForms.css';
|
import './AuthForms.css';
|
||||||
|
|
||||||
function Register() {
|
function Register() {
|
||||||
|
// Состояние для хранения данных формы
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Хук для навигации между страницами
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Обработчик изменений в полях ввода
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target; // Извлекаем имя поля и значение
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev, // Копируем предыдущее состояние
|
||||||
[name]: value
|
[name]: value // Обновляем конкретное поле
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Обработчик отправки формы
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault(); // Отменяем стандартное поведение формы
|
||||||
|
|
||||||
|
// В реальном приложении здесь будет запрос к API
|
||||||
console.log('Регистрация:', formData);
|
console.log('Регистрация:', formData);
|
||||||
|
|
||||||
|
// Перенаправляем на главную страницу
|
||||||
navigate('/');
|
navigate('/');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,18 +37,21 @@ function Register() {
|
|||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<div className="auth-container">
|
<div className="auth-container">
|
||||||
<h2>Регистрация</h2>
|
<h2>Регистрация</h2>
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
|
<form onSubmit={handleSubmit}> {/* Форма с обработчиком отправки */}
|
||||||
|
{/* Группа полей для имени */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Имя</label>
|
<label>Имя</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
value={formData.name}
|
value={formData.name} // Привязка к состоянию
|
||||||
onChange={handleChange}
|
onChange={handleChange} // Обработчик изменений
|
||||||
required
|
required // Обязательное поле
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Группа полей для email */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Email</label>
|
<label>Email</label>
|
||||||
<input
|
<input
|
||||||
@@ -52,6 +63,7 @@ function Register() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Группа полей для пароля */}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Пароль</label>
|
<label>Пароль</label>
|
||||||
<input
|
<input
|
||||||
@@ -63,11 +75,13 @@ function Register() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Кнопка отправки формы */}
|
||||||
<button type="submit" className="auth-btn">
|
<button type="submit" className="auth-btn">
|
||||||
Зарегистрироваться
|
Зарегистрироваться
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{/* Ссылка для перехода к авторизации */}
|
||||||
<div className="auth-switch">
|
<div className="auth-switch">
|
||||||
<span>Уже есть аккаунт? <a href="/login">Войти</a></span>
|
<span>Уже есть аккаунт? <a href="/login">Войти</a></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user