Skip to content

Commit 4396fdf

Browse files
authored
feat: built header for explore project section (#292)
1 parent 69faafc commit 4396fdf

4 files changed

Lines changed: 237 additions & 8 deletions

File tree

app/(landing)/projects/layout.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Metadata } from 'next';
2+
import { generatePageMetadata } from '@/lib/metadata';
3+
4+
export const metadata: Metadata = generatePageMetadata('projects');
5+
6+
export default function ProjectsLayout({
7+
children,
8+
}: {
9+
children: React.ReactNode;
10+
}) {
11+
return <>{children}</>;
12+
}

app/(landing)/projects/page.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
1+
'use client';
2+
13
import React from 'react';
2-
import { Metadata } from 'next';
3-
import { generatePageMetadata } from '@/lib/metadata';
44
import ProjectPageHero from '@/components/Project-Page-Hero';
5+
import ExploreHeader from '@/components/projects/ExploreHeader';
6+
7+
function ProjectsPage() {
8+
const handleSearch = (searchTerm: string) => {
9+
console.log('Search:', searchTerm);
10+
};
11+
12+
const handleSort = (sortType: string) => {
13+
console.log('Sort:', sortType);
14+
};
15+
16+
const handleStatus = (status: string) => {
17+
console.log('Status:', status);
18+
};
519

6-
export const metadata: Metadata = generatePageMetadata('projects');
20+
const handleCategory = (category: string) => {
21+
console.log('Category:', category);
22+
};
723

8-
const ProjectsPage = () => {
924
return (
10-
<div className=''>
25+
<div>
1126
<ProjectPageHero />
12-
<h1 className='mb-10'>Projects</h1>
13-
<div className='flex flex-wrap justify-center gap-8'></div>
27+
<ExploreHeader
28+
onSearch={handleSearch}
29+
onSortChange={handleSort}
30+
onStatusChange={handleStatus}
31+
onCategoryChange={handleCategory}
32+
/>
33+
<div className='flex flex-wrap justify-center gap-8 p-8'></div>
1434
</div>
1535
);
16-
};
36+
}
1737

1838
export default ProjectsPage;
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import { Search, ChevronDown } from 'lucide-react';
5+
import { Input } from '@/components/ui/input';
6+
import { Button } from '@/components/ui/button';
7+
import {
8+
DropdownMenu,
9+
DropdownMenuContent,
10+
DropdownMenuItem,
11+
DropdownMenuTrigger,
12+
} from '@/components/ui/dropdown-menu';
13+
import { cn } from '@/lib/utils';
14+
import Image from 'next/image';
15+
16+
interface ExploreHeaderProps {
17+
onSearch?: (searchTerm: string) => void;
18+
onSortChange?: (sortType: string) => void;
19+
onStatusChange?: (status: string) => void;
20+
onCategoryChange?: (category: string) => void;
21+
className?: string;
22+
}
23+
24+
const ExploreHeader = ({
25+
onSearch,
26+
onSortChange,
27+
onStatusChange,
28+
onCategoryChange,
29+
className,
30+
}: ExploreHeaderProps) => {
31+
const [searchTerm, setSearchTerm] = useState('');
32+
const [selectedSort, setSelectedSort] = useState('Sort');
33+
const [selectedStatus, setSelectedStatus] = useState('Status');
34+
const [selectedCategory, setSelectedCategory] = useState('Category');
35+
36+
const handleSearch = (value: string) => {
37+
setSearchTerm(value);
38+
if (onSearch) onSearch(value);
39+
};
40+
41+
const handleSort = (sortType: string) => {
42+
setSelectedSort(sortType);
43+
if (onSortChange) onSortChange(sortType);
44+
};
45+
46+
const handleStatus = (status: string) => {
47+
setSelectedStatus(status);
48+
if (onStatusChange) onStatusChange(status);
49+
};
50+
51+
const handleCategory = (category: string) => {
52+
setSelectedCategory(category);
53+
if (onCategoryChange) onCategoryChange(category);
54+
};
55+
56+
const sortOptions = [
57+
'Newest First',
58+
'Oldest First',
59+
'Most Funded',
60+
'Least Funded',
61+
'Ending Soon',
62+
'Recently Started',
63+
];
64+
const statusOptions = [
65+
'All Status',
66+
'Active',
67+
'Completed',
68+
'Draft',
69+
'Under Review',
70+
'Validated',
71+
'Approved',
72+
];
73+
const categoryOptions = [
74+
'All Categories',
75+
'Technology',
76+
'Art & Creative',
77+
'Environment',
78+
'Education',
79+
'Healthcare',
80+
'Community',
81+
'DeFi',
82+
'NFT',
83+
'Web3',
84+
];
85+
86+
return (
87+
<div className={cn('w-full bg-black px-4 py-12 text-white', className)}>
88+
<div className='mx-auto max-w-7xl'>
89+
<div className='mb-12'>
90+
<h1 className='font-inter text-center text-4xl font-bold text-white md:text-left md:text-5xl lg:text-6xl'>
91+
Explore Boundless Projects
92+
</h1>
93+
</div>
94+
95+
<div className='flex flex-col items-start gap-6 lg:flex-row lg:items-center lg:gap-8'>
96+
<div className='flex flex-wrap items-center gap-3 lg:gap-4'>
97+
<DropdownMenu>
98+
<DropdownMenuTrigger asChild>
99+
<Button
100+
variant='outline'
101+
className='min-w-[100px] justify-between rounded-lg border-gray-600 bg-transparent px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 hover:text-white'
102+
>
103+
<div className='flex items-center gap-2'>
104+
<Image
105+
src='/sort.svg'
106+
alt='Sort'
107+
width={16}
108+
height={16}
109+
className='h-4 w-4'
110+
/>
111+
{selectedSort}
112+
</div>
113+
<ChevronDown className='ml-2 h-4 w-4' />
114+
</Button>
115+
</DropdownMenuTrigger>
116+
<DropdownMenuContent className='border-gray-700 bg-gray-900 text-white'>
117+
{sortOptions.map(option => (
118+
<DropdownMenuItem
119+
key={option}
120+
onClick={() => handleSort(option)}
121+
className='cursor-pointer hover:bg-gray-800'
122+
>
123+
{option}
124+
</DropdownMenuItem>
125+
))}
126+
</DropdownMenuContent>
127+
</DropdownMenu>
128+
129+
<DropdownMenu>
130+
<DropdownMenuTrigger asChild>
131+
<Button
132+
variant='outline'
133+
className='min-w-[100px] justify-between rounded-lg border-gray-600 bg-transparent px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 hover:text-white'
134+
>
135+
{selectedStatus}
136+
<ChevronDown className='ml-2 h-4 w-4' />
137+
</Button>
138+
</DropdownMenuTrigger>
139+
<DropdownMenuContent className='border-gray-700 bg-gray-900 text-white'>
140+
{statusOptions.map(option => (
141+
<DropdownMenuItem
142+
key={option}
143+
onClick={() => handleStatus(option)}
144+
className='cursor-pointer hover:bg-gray-800'
145+
>
146+
{option}
147+
</DropdownMenuItem>
148+
))}
149+
</DropdownMenuContent>
150+
</DropdownMenu>
151+
152+
<DropdownMenu>
153+
<DropdownMenuTrigger asChild>
154+
<Button
155+
variant='outline'
156+
className='min-w-[100px] justify-between rounded-lg border-gray-600 bg-transparent px-4 py-2 text-sm font-medium text-white hover:bg-gray-800 hover:text-white'
157+
>
158+
{selectedCategory}
159+
<ChevronDown className='ml-2 h-4 w-4' />
160+
</Button>
161+
</DropdownMenuTrigger>
162+
<DropdownMenuContent className='border-gray-700 bg-gray-900 text-white'>
163+
{categoryOptions.map(option => (
164+
<DropdownMenuItem
165+
key={option}
166+
onClick={() => handleCategory(option)}
167+
className='cursor-pointer hover:bg-gray-800'
168+
>
169+
{option}
170+
</DropdownMenuItem>
171+
))}
172+
</DropdownMenuContent>
173+
</DropdownMenu>
174+
</div>
175+
176+
<div className='relative ml-auto w-full lg:max-w-md'>
177+
<div className='relative'>
178+
<Search className='absolute top-1/2 left-3 h-5 w-5 -translate-y-1/2 transform text-gray-400' />
179+
<Input
180+
type='text'
181+
placeholder='Search project or creator...'
182+
value={searchTerm}
183+
onChange={e => handleSearch(e.target.value)}
184+
className='w-full rounded-lg border-gray-600 bg-transparent py-3 pr-4 pl-10 text-base text-white placeholder-gray-400 focus:border-gray-400 focus:ring-1 focus:ring-gray-400'
185+
/>
186+
</div>
187+
</div>
188+
</div>
189+
</div>
190+
</div>
191+
);
192+
};
193+
194+
export default ExploreHeader;

public/sort.svg

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)