diff --git a/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.module.scss b/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.module.scss new file mode 100644 index 0000000..7b4114c --- /dev/null +++ b/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.module.scss @@ -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; +} diff --git a/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.tsx b/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.tsx index f244c22..8a91bcd 100644 --- a/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.tsx +++ b/apps/web/src/app/brand/[id]/catalog/BrandCatalogContent.tsx @@ -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 = [ - { label: 'Главная', href: '/' }, - { label: 'Каталог', href: '/catalog' }, - { label: brand.name, href: `/brand/${id}` }, - { label: 'Каталог' }, - ]; - return ( - +
+
+ + {filtered.length} позиций +
+
+ {filtered.map((p) => ( + + ))} +
+ {filtered.length === 0 && ( +

+ По выбранным фильтрам ничего не найдено. +

+ )} +
); } diff --git a/apps/web/src/app/brand/[id]/catalog/page.module.scss b/apps/web/src/app/brand/[id]/catalog/page.module.scss new file mode 100644 index 0000000..b32ed28 --- /dev/null +++ b/apps/web/src/app/brand/[id]/catalog/page.module.scss @@ -0,0 +1,7 @@ +.layout { + max-width: var(--max-width); + margin: 0 auto; + padding: var(--space-lg); + display: flex; + gap: var(--space-lg); +} diff --git a/apps/web/src/app/brand/[id]/catalog/page.tsx b/apps/web/src/app/brand/[id]/catalog/page.tsx index 3a31b3a..85617b7 100644 --- a/apps/web/src/app/brand/[id]/catalog/page.tsx +++ b/apps/web/src/app/brand/[id]/catalog/page.tsx @@ -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 ( - }> - - +
+ + }> + + +
); } diff --git a/apps/web/src/app/catalog/CatalogContent.module.scss b/apps/web/src/app/catalog/CatalogContent.module.scss new file mode 100644 index 0000000..7b4114c --- /dev/null +++ b/apps/web/src/app/catalog/CatalogContent.module.scss @@ -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; +} diff --git a/apps/web/src/app/catalog/CatalogContent.tsx b/apps/web/src/app/catalog/CatalogContent.tsx index 89b4a25..5256f12 100644 --- a/apps/web/src/app/catalog/CatalogContent.tsx +++ b/apps/web/src/app/catalog/CatalogContent.tsx @@ -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 = [ - { label: 'Главная', href: '/' }, - { label: 'Каталог' }, - ]; - return ( - +
+
+ + {filtered.length} позиций +
+
+ {filtered.map((p) => ( + + ))} +
+ {filtered.length === 0 && ( +

+ По выбранным фильтрам ничего не найдено. +

+ )} +
); } diff --git a/apps/web/src/app/catalog/page.module.scss b/apps/web/src/app/catalog/page.module.scss new file mode 100644 index 0000000..b32ed28 --- /dev/null +++ b/apps/web/src/app/catalog/page.module.scss @@ -0,0 +1,7 @@ +.layout { + max-width: var(--max-width); + margin: 0 auto; + padding: var(--space-lg); + display: flex; + gap: var(--space-lg); +} diff --git a/apps/web/src/app/catalog/page.tsx b/apps/web/src/app/catalog/page.tsx index 4926b35..803da1c 100644 --- a/apps/web/src/app/catalog/page.tsx +++ b/apps/web/src/app/catalog/page.tsx @@ -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 ( - }> - - +
+ + }> + + +
); } diff --git a/apps/web/src/components/Skeleton/Skeleton.module.scss b/apps/web/src/components/Skeleton/Skeleton.module.scss index eadd90f..a2dccb3 100644 --- a/apps/web/src/components/Skeleton/Skeleton.module.scss +++ b/apps/web/src/components/Skeleton/Skeleton.module.scss @@ -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; } diff --git a/apps/web/src/components/Skeleton/Skeleton.tsx b/apps/web/src/components/Skeleton/Skeleton.tsx index ed2e481..62af89a 100644 --- a/apps/web/src/components/Skeleton/Skeleton.tsx +++ b/apps/web/src/components/Skeleton/Skeleton.tsx @@ -30,15 +30,12 @@ export function BrandLogosSkeleton() { export function CatalogSkeleton() { return ( -
- -
- -
- {Array.from({ length: 6 }).map((_, i) => ( - - ))} -
+
+ +
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))}
);