2 Commits

Author SHA1 Message Date
b7aa56e629 done 2025-10-23 12:56:06 +04:00
ca3a9c8aee доп принята 2025-10-09 14:46:34 +04:00
5 changed files with 142 additions and 9 deletions

BIN
img/рикрол.mp4 Normal file

Binary file not shown.

View File

@@ -17,8 +17,22 @@ body {
min-height: 100vh;
}
/* Главный контейнер для контента */
main {
flex: 1;
display: flex;
flex-direction: column;
}
/* Стили для страницы ошибки */
.error-page {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 2rem;
}
/* Стили для навигации */
@@ -80,4 +94,25 @@ main {
.card:hover {
transform: translateY(-5px);
}
/* Стили для панели сортировки */
.sort-panel {
background-color: #f8f9fa;
border-radius: 8px;
padding: 15px;
}
.sort-select {
border-color: #00264d;
}
.sort-select:focus {
border-color: #00264d;
box-shadow: 0 0 0 0.2rem rgba(0, 38, 77, 0.25);
}
.sort-info {
background-color: #e3f2fd;
border-left: 4px solid #00264d;
}

View File

@@ -8,6 +8,7 @@ import CatalogPage from './pages/CatalogPage.jsx';
import ContactsPage from './pages/ContactsPage.jsx';
import LikesPage from './pages/LikesPage.jsx';
import BasketPage from './pages/BasketPage.jsx';
import ErrorPage from './pages/ErrorPage.jsx';
import Footer from './components/Footer.jsx';
import Navbar from './components/Navbar.jsx';
@@ -20,13 +21,16 @@ export default function App() {
<ToastContainer />
<BrowserRouter>
<Navbar />
<Routes>
<Route path="/" element={<IndexPage />} />
<Route path="/catalog" element={<CatalogPage />} />
<Route path="/contacts" element={<ContactsPage />} />
<Route path="/likes" element={<LikesPage />} />
<Route path="/basket" element={<BasketPage />} />
</Routes>
<main style={{ flex: 1 }}>
<Routes>
<Route path="/" element={<IndexPage />} />
<Route path="/catalog" element={<CatalogPage />} />
<Route path="/contacts" element={<ContactsPage />} />
<Route path="/likes" element={<LikesPage />} />
<Route path="/basket" element={<BasketPage />} />
<Route path="*" element={<ErrorPage/>}/>
</Routes>
</main>
<Footer />
</BrowserRouter>
</LikesProvider>

View File

@@ -19,6 +19,23 @@ export default function CatalogPage() {
} = useProducts();
const [showAddForm, setShowAddForm] = useState(false);
const [sortBy, setSortBy] = useState(''); // '' - без сортировки, 'price_asc' - по возрастанию, 'price_desc' - по убыванию
// Функция для сортировки товаров
const getSortedProducts = () => {
const productsCopy = [...products];
switch (sortBy) {
case 'price_asc':
return productsCopy.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
case 'price_desc':
return productsCopy.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
default:
return productsCopy;
}
};
const sortedProducts = getSortedProducts();
const handleAddProduct = async (productData) => {
try {
@@ -47,6 +64,10 @@ export default function CatalogPage() {
}
};
const handleSortChange = (e) => {
setSortBy(e.target.value);
};
if (loading) {
return (
<main className="container my-4">
@@ -83,6 +104,51 @@ export default function CatalogPage() {
</button>
</div>
{/* Панель сортировки */}
<div className="row mb-4">
<div className="col-md-6">
<div className="card border-0 shadow-sm">
<div className="card-body">
<div className="row align-items-center">
<div className="col-auto">
<label htmlFor="sortSelect" className="col-form-label">
<i className="bi bi-sort-down me-2"></i>Сортировка:
</label>
</div>
<div className="col">
<select
id="sortSelect"
className="form-select"
value={sortBy}
onChange={handleSortChange}
>
<option value="">Без сортировки</option>
<option value="price_asc">Цена по возрастанию</option>
<option value="price_desc">Цена по убыванию</option>
</select>
</div>
</div>
</div>
</div>
</div>
{/* Информация о сортировке */}
<div className="col-md-6">
{sortBy && (
<div className="card border-0 shadow-sm">
<div className="card-body py-2">
<small className="text-muted">
<i className="bi bi-info-circle me-1"></i>
{sortBy === 'price_asc' && 'Сортировка: от дешевых к дорогим'}
{sortBy === 'price_desc' && 'Сортировка: от дорогих к дешевым'}
{sortedProducts.length > 0 && ` (${sortedProducts.length} товаров)`}
</small>
</div>
</div>
)}
</div>
</div>
{showAddForm && (
<div className="row mb-4">
<div className="col-12">
@@ -99,7 +165,7 @@ export default function CatalogPage() {
{/* 3 карточки в ряд на всех экранах кроме мобильных */}
<div className="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mb-5">
{products.map(product => (
{sortedProducts.map(product => (
<ProductCard
key={product.id}
product={product}
@@ -113,7 +179,7 @@ export default function CatalogPage() {
))}
</div>
{products.length === 0 && !showAddForm && (
{sortedProducts.length === 0 && !showAddForm && (
<div className="text-center py-5">
<h3>Товаров пока нет</h3>
<p className="text-muted">Добавьте первый товар в каталог</p>

28
src/Pages/ErrorPage.jsx Normal file
View File

@@ -0,0 +1,28 @@
export default function ErrorPage() {
return (
<div className="error-page" style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '60vh',
textAlign: 'center',
padding: '2rem'
}}>
<h1>404 - Страница не найдена</h1>
<p>Извините, запрашиваемая страница не существует.</p>
<video
src="./img/рикрол.mp4"
autoPlay
loop
style={{
maxWidth: '100%',
height: 'auto',
borderRadius: '8px',
marginTop: '1rem'
}}
>
</video>
</div>
);
}