mirror of
https://github.com/Nezumi-2711/multitenant-ecommerce.git
synced 2026-09-22 13:38:34 +00:00
feat: add category pages
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
interface Props {
|
||||
params: Promise<{
|
||||
category: string;
|
||||
subcategory: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const Page = async ({ params }: Props) => {
|
||||
const { category, subcategory } = await params;
|
||||
|
||||
return <div>Subcategory Page: {subcategory} in Category: {category}</div>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,13 @@
|
||||
interface Props {
|
||||
params: Promise<{
|
||||
category: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const Page = async ({ params }: Props) => {
|
||||
const { category } = await params;
|
||||
|
||||
return <div>Category Page: {category}</div>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -3,9 +3,9 @@ import { dehydrate, HydrationBoundary } from '@tanstack/react-query';
|
||||
|
||||
import { getQueryClient, trpc } from '@/trpc/server';
|
||||
|
||||
import Navbar from './navbar';
|
||||
import Footer from './footer';
|
||||
import SearchFilters, { SearchFiltersSkeleton } from './search-filters';
|
||||
import Navbar from '@/modules/home/ui/components/navbar';
|
||||
import Footer from '@/modules/home/ui/components/footer';
|
||||
import SearchFilters, { SearchFiltersSkeleton } from '@/modules/home/ui/components/search-filters';
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useTRPC } from '@/trpc/client';
|
||||
|
||||
import Categories from './categories';
|
||||
import SearchInput from './search-input';
|
||||
|
||||
const SearchFilters = () => {
|
||||
const trpc = useTRPC();
|
||||
const { data } = useSuspenseQuery(trpc.categories.getMany.queryOptions());
|
||||
|
||||
return (
|
||||
<div className="px-4 lg:px-12 py-8 border-b flex flex-col gap-4 w-full bg-[#F5F5F5]">
|
||||
<SearchInput />
|
||||
|
||||
<div className="hidden lg:block">
|
||||
<Categories data={data} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SearchFiltersSkeleton = () => {
|
||||
return (
|
||||
<div className="px-4 lg:px-12 py-8 border-b flex flex-col gap-4 w-full bg-[#F5F5F5]">
|
||||
<SearchInput disabled />
|
||||
|
||||
<div className="hidden lg:block">
|
||||
<div className="h-11" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilters;
|
||||
@@ -0,0 +1 @@
|
||||
export const DEFAULT_BG_COLOR = '#F5F5F5';
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Props {
|
||||
activeCategoryName?: string | null;
|
||||
activeCategory?: string | null;
|
||||
activeSubCategoryName?: string | null;
|
||||
}
|
||||
|
||||
export const BreadcrumbNavigation = ({
|
||||
activeCategory,
|
||||
activeCategoryName,
|
||||
activeSubCategoryName,
|
||||
}: Props) => {
|
||||
if (!activeCategoryName || activeCategory === 'all') return null;
|
||||
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{activeSubCategoryName ? (
|
||||
<>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
asChild
|
||||
className="text-xl font-medium underline text-primary"
|
||||
>
|
||||
<Link href={`/${activeCategory}`}>{activeCategoryName}</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator className="text-primary font-medium text-lg">
|
||||
/
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage className="text-xl font-medium">
|
||||
{activeSubCategoryName}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</>
|
||||
) : (
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage className="text-xl font-medium">
|
||||
{activeCategoryName}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
)}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
);
|
||||
};
|
||||
+8
-3
@@ -1,20 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ListFilterIcon } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { CategoriesGetManyOutput } from '@/modules/categories/type';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
import CategoryDropdown from './category-dropdown';
|
||||
|
||||
import CategoriesSidebar from './categories-sidebar';
|
||||
import { CategoriesGetManyOutput } from '@/modules/categories/type';
|
||||
|
||||
interface CategoriesProps {
|
||||
data: CategoriesGetManyOutput;
|
||||
}
|
||||
|
||||
const Categories = ({ data }: CategoriesProps) => {
|
||||
const params = useParams();
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const measureRef = useRef<HTMLDivElement>(null);
|
||||
const viewAllRef = useRef<HTMLDivElement>(null);
|
||||
@@ -23,7 +26,8 @@ const Categories = ({ data }: CategoriesProps) => {
|
||||
const [isAnyHovered, setIsAnyHovered] = useState(false);
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
||||
|
||||
const activeCategory = 'all';
|
||||
const categoryParam = params.category as string | undefined;
|
||||
const activeCategory = categoryParam || 'all';
|
||||
|
||||
const activeCategoryIndex = data.findIndex(
|
||||
(cat) => cat.slug === activeCategory
|
||||
@@ -107,6 +111,7 @@ const Categories = ({ data }: CategoriesProps) => {
|
||||
|
||||
<div ref={viewAllRef} className="shrink-0">
|
||||
<Button
|
||||
variant="elevated"
|
||||
className={cn(
|
||||
'h-11 px-4 bg-transparent border-transparent rounded-full hover:bg-white hover:border-primary text-black',
|
||||
isActiveCategoryHidden &&
|
||||
@@ -0,0 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
import { useTRPC } from '@/trpc/client';
|
||||
|
||||
import Categories from './categories';
|
||||
import SearchInput from './search-input';
|
||||
import { DEFAULT_BG_COLOR } from '../constants';
|
||||
import { BreadcrumbNavigation } from './breadcrumb-navigation';
|
||||
|
||||
const SearchFilters = () => {
|
||||
const trpc = useTRPC();
|
||||
const { data } = useSuspenseQuery(trpc.categories.getMany.queryOptions());
|
||||
const params = useParams();
|
||||
const categoryParam = params.category as string | undefined;
|
||||
const activeCategory = categoryParam || 'all';
|
||||
const activeCategoryData = data.find(
|
||||
(category) => category.slug === activeCategory
|
||||
);
|
||||
const activeCategoryColor = activeCategoryData?.color || DEFAULT_BG_COLOR;
|
||||
const activeCategoryName = activeCategoryData?.name || null;
|
||||
const activeSubCategory = params.subcategory as string | undefined;
|
||||
const activeSubCategoryName =
|
||||
activeCategoryData?.subcategories.find(
|
||||
(sub) => sub.slug === activeSubCategory
|
||||
)?.name || null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="px-4 lg:px-12 py-8 border-b flex flex-col gap-4 w-full bg-[#F5F5F5]"
|
||||
style={{
|
||||
backgroundColor: activeCategoryColor,
|
||||
}}
|
||||
>
|
||||
<SearchInput />
|
||||
|
||||
<div className="hidden lg:block">
|
||||
<Categories data={data} />
|
||||
</div>
|
||||
|
||||
<BreadcrumbNavigation
|
||||
activeCategory={activeCategory}
|
||||
activeCategoryName={activeCategoryName}
|
||||
activeSubCategoryName={activeSubCategoryName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SearchFiltersSkeleton = () => {
|
||||
return (
|
||||
<div className="px-4 lg:px-12 py-8 border-b flex flex-col gap-4 w-full bg-[#F5F5F5]">
|
||||
<SearchInput disabled />
|
||||
|
||||
<div className="hidden lg:block">
|
||||
<div className="h-11" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilters;
|
||||
Reference in New Issue
Block a user