This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
.main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 12px;
|
||||
color: var(--color-slate-400);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-md);
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
padding: var(--space-2xl) 0;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { filterProducts, parseSearchParams } from '@/lib/filterProducts';
|
||||
import { CatalogView } from '@/components/CatalogView/CatalogView';
|
||||
import { Breadcrumb } from '@/components/Breadcrumb/Breadcrumb';
|
||||
import { ProductCard } from '@/components/ProductCard/ProductCard';
|
||||
import { getBrands, getProducts } from '@/lib/api';
|
||||
import styles from './BrandCatalogContent.module.scss';
|
||||
|
||||
export async function BrandCatalogContent({
|
||||
id,
|
||||
@@ -21,21 +23,29 @@ export async function BrandCatalogContent({
|
||||
const brandProducts = products.filter((p) => p.brand === brand.name);
|
||||
const filtered = filterProducts(brandProducts, filters);
|
||||
|
||||
const basePath = `/brand/${id}/catalog`;
|
||||
|
||||
const breadcrumbItems = [
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className={styles.header}>
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ label: 'Главная', href: '/' },
|
||||
{ label: 'Каталог', href: '/catalog' },
|
||||
{ label: brand.name, href: `/brand/${id}` },
|
||||
{ label: 'Каталог' },
|
||||
];
|
||||
|
||||
return (
|
||||
<CatalogView
|
||||
products={filtered}
|
||||
breadcrumbItems={breadcrumbItems}
|
||||
basePath={basePath}
|
||||
hiddenFilters={['brand']}
|
||||
]}
|
||||
/>
|
||||
<span className={styles.count}>{filtered.length} позиций</span>
|
||||
</div>
|
||||
<div className={styles.grid}>
|
||||
{filtered.map((p) => (
|
||||
<ProductCard key={p.id} product={p} />
|
||||
))}
|
||||
</div>
|
||||
{filtered.length === 0 && (
|
||||
<p className={styles.empty}>
|
||||
По выбранным фильтрам ничего не найдено.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
7
apps/web/src/app/brand/[id]/catalog/page.module.scss
Normal file
7
apps/web/src/app/brand/[id]/catalog/page.module.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.layout {
|
||||
max-width: var(--max-width);
|
||||
margin: 0 auto;
|
||||
padding: var(--space-lg);
|
||||
display: flex;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Suspense } from 'react';
|
||||
import { getBrands } from '@/lib/api';
|
||||
import { CatalogSidebar } from '@/components/CatalogSidebar/CatalogSidebar';
|
||||
import { CatalogSkeleton } from '@/components/Skeleton/Skeleton';
|
||||
import { BrandCatalogContent } from './BrandCatalogContent';
|
||||
import styles from './page.module.scss';
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -28,10 +30,14 @@ export default async function BrandCatalogPage({
|
||||
const { id } = await params;
|
||||
const sp = await searchParams;
|
||||
const key = JSON.stringify(sp);
|
||||
const basePath = `/brand/${id}/catalog`;
|
||||
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
<CatalogSidebar basePath={basePath} hiddenFilters={['brand']} />
|
||||
<Suspense key={key} fallback={<CatalogSkeleton />}>
|
||||
<BrandCatalogContent id={id} params={sp} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
35
apps/web/src/app/catalog/CatalogContent.module.scss
Normal file
35
apps/web/src/app/catalog/CatalogContent.module.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
.main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 12px;
|
||||
color: var(--color-slate-400);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-md);
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
padding: var(--space-2xl) 0;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { filterProducts, parseSearchParams } from '@/lib/filterProducts';
|
||||
import { CatalogView } from '@/components/CatalogView/CatalogView';
|
||||
import { Breadcrumb } from '@/components/Breadcrumb/Breadcrumb';
|
||||
import { ProductCard } from '@/components/ProductCard/ProductCard';
|
||||
import { getProducts } from '@/lib/api';
|
||||
import styles from './CatalogContent.module.scss';
|
||||
|
||||
export async function CatalogContent({
|
||||
params,
|
||||
@@ -11,15 +13,27 @@ export async function CatalogContent({
|
||||
const filters = parseSearchParams(params);
|
||||
const filtered = filterProducts(products, filters);
|
||||
|
||||
const breadcrumbItems = [
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className={styles.header}>
|
||||
<Breadcrumb
|
||||
items={[
|
||||
{ label: 'Главная', href: '/' },
|
||||
{ label: 'Каталог' },
|
||||
];
|
||||
|
||||
return (
|
||||
<CatalogView
|
||||
products={filtered}
|
||||
breadcrumbItems={breadcrumbItems}
|
||||
]}
|
||||
/>
|
||||
<span className={styles.count}>{filtered.length} позиций</span>
|
||||
</div>
|
||||
<div className={styles.grid}>
|
||||
{filtered.map((p) => (
|
||||
<ProductCard key={p.id} product={p} />
|
||||
))}
|
||||
</div>
|
||||
{filtered.length === 0 && (
|
||||
<p className={styles.empty}>
|
||||
По выбранным фильтрам ничего не найдено.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
7
apps/web/src/app/catalog/page.module.scss
Normal file
7
apps/web/src/app/catalog/page.module.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
.layout {
|
||||
max-width: var(--max-width);
|
||||
margin: 0 auto;
|
||||
padding: var(--space-lg);
|
||||
display: flex;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Suspense } from 'react';
|
||||
import { categoryNameMap, CategorySlug } from '@/types';
|
||||
import { CatalogSidebar } from '@/components/CatalogSidebar/CatalogSidebar';
|
||||
import { CatalogSkeleton } from '@/components/Skeleton/Skeleton';
|
||||
import { CatalogContent } from './CatalogContent';
|
||||
import styles from './page.module.scss';
|
||||
|
||||
export async function generateMetadata({
|
||||
searchParams,
|
||||
@@ -30,8 +32,11 @@ export default async function CatalogPage({
|
||||
const key = JSON.stringify(params);
|
||||
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
<CatalogSidebar />
|
||||
<Suspense key={key} fallback={<CatalogSkeleton />}>
|
||||
<CatalogContent params={params} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,33 +59,22 @@
|
||||
|
||||
.catalogGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md) 0;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.catalogCard {
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.catalogLayout {
|
||||
display: flex;
|
||||
gap: var(--space-xl);
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: var(--space-xl);
|
||||
}
|
||||
|
||||
.catalogSidebar {
|
||||
width: 240px;
|
||||
height: 400px;
|
||||
flex-shrink: 0;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.catalogMain {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ export function BrandLogosSkeleton() {
|
||||
|
||||
export function CatalogSkeleton() {
|
||||
return (
|
||||
<div className={styles.catalogLayout}>
|
||||
<Box className={styles.catalogSidebar} />
|
||||
<div className={styles.catalogMain}>
|
||||
<Box className={styles.titleBar} />
|
||||
<div className={styles.catalogGrid}>
|
||||
@@ -40,7 +38,6 @@ export function CatalogSkeleton() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user