feat: implement explore page

This commit is contained in:
jinhojang6 2024-09-13 02:18:43 +09:00
parent e42c224b86
commit b896eca534
15 changed files with 407 additions and 37 deletions

View File

@ -0,0 +1,81 @@
import { Checkbox } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import React, { useState } from 'react'
interface FilterOption {
label: string
}
const filterOptions: FilterOption[] = [
{
label: 'All',
},
{
label: 'Archetype',
},
{
label: 'Comp',
},
{
label: 'Helmet',
},
{
label: 'Jacket',
},
{
label: 'Background',
},
]
interface OperatorFilterProps {}
const OperatorFilter: React.FC<OperatorFilterProps> = () => {
// checkbox based on filterOptions
const [checked, setChecked] = useState<number | null>(null)
return (
<Container>
{filterOptions.map((option, index) => (
<FilterOption key={index}>
<Checkbox
checked={index === checked}
onChange={() => setChecked(index)}
/>
<span className="label">{option.label}</span>
</FilterOption>
))}
</Container>
)
}
const Container = styled.div`
display: flex;
margin-top: 74px;
align-items: start;
gap: 2px;
justify-content: start;
flex-wrap: wrap;
margin: 74px auto 0 auto;
.label {
display: flex;
padding: 6px 16px;
justify-content: center;
align-items: center;
font-size: 14px;
line-height: 20px;
}
@media (max-width: 991px) {
max-width: 100%;
margin-top: 40px;
}
`
const FilterOption = styled.div`
display: flex;
padding: 8px 23px 8px 8px;
align-items: center;
background: var(--grey-900);
`
export default OperatorFilter

View File

@ -0,0 +1 @@
export { default as OperatorFilter } from './OperatorFilter'

View File

@ -0,0 +1,105 @@
import { breakpoints } from '@/configs/ui.configs'
import styled from '@emotion/styled'
import Link from 'next/link'
import React from 'react'
interface OperatorGridProps {}
const operatorImages = [
{
image: '/dashboard/mock/operators/1.gif',
},
{
image: '/dashboard/mock/operators/2.gif',
},
{
image: '/dashboard/mock/operators/3.gif',
},
{
image: '/dashboard/mock/operators/4.gif',
},
{
image: '/dashboard/mock/operators/5.gif',
},
{
image: '/dashboard/mock/operators/6.gif',
},
{
image: '/dashboard/mock/operators/7.gif',
},
{
image: '/dashboard/mock/operators/1.gif',
},
{
image: '/dashboard/mock/operators/2.gif',
},
{
image: '/dashboard/mock/operators/3.gif',
},
{
image: '/dashboard/mock/operators/4.gif',
},
{
image: '/dashboard/mock/operators/5.gif',
},
{
image: '/dashboard/mock/operators/6.gif',
},
{
image: '/dashboard/mock/operators/7.gif',
},
{
image: '/dashboard/mock/operators/1.gif',
},
]
const OperatorGrid: React.FC<OperatorGridProps> = () => {
return (
<StyledOperatorGrid>
{operatorImages.map((operator, index) => (
<Link href={`/explore/${index + 1}`} key={'explore-operator-' + index}>
<GridItem>
<img
key={index}
src={operator.image}
alt={`Operator ${index + 1}`}
loading="lazy"
/>
</GridItem>
</Link>
))}
</StyledOperatorGrid>
)
}
// grid: 6 responsive columns
const StyledOperatorGrid = styled.section`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
gap: 16px;
margin-top: 16px;
@media (max-width: ${breakpoints.md}px) {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: ${breakpoints.sm}px) {
grid-template-columns: repeat(2, 1fr);
}
img {
aspect-ratio: 1;
object-fit: contain;
width: 216px;
}
`
const GridItem = styled.div`
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
`
export default OperatorGrid

View File

@ -0,0 +1 @@
export { default as OperatorGrid } from './OperatorGrid'

View File

@ -0,0 +1,55 @@
import styled from '@emotion/styled'
import React from 'react'
interface FooterProps {}
const Footer: React.FC<FooterProps> = () => {
return (
<StyledFooter>
<span className="logo">Logos Operators</span>
<nav className="footer-nav">
<a href="#terms">Terms of Use</a>
<a href="#privacy">Privacy Policy</a>
<a href="#discord">Discord</a>
<a href="#manifesto">Manifesto</a>
</nav>
<span className="copyright">
All right reserved ©{new Date().getFullYear()}
</span>
</StyledFooter>
)
}
const StyledFooter = styled.footer`
display: flex;
margin-top: 200px;
width: 100%;
align-items: center;
justify-content: space-between;
font-size: 12px;
line-height: 16px;
position: relative;
.footer-nav {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
display: flex;
min-width: 240px;
align-items: center;
gap: 32px;
}
.footer-nav a {
color: inherit;
text-decoration: none;
}
@media (max-width: 991px) {
max-width: 100%;
margin-top: 40px;
}
`
export default Footer

View File

@ -0,0 +1 @@
export { default as Footer } from './Footer'

View File

@ -1,4 +1,5 @@
import styled from '@emotion/styled'
import Link from 'next/link'
import React from 'react'
import { Navbar } from '../Navbar'
@ -7,7 +8,9 @@ interface NavbarProps {}
const Header: React.FC<NavbarProps> = () => {
return (
<Container>
<Link href="/">
<Logo src="/assets/logo.svg" alt="Logo" />
</Link>
<Navbar />
<UserActions>
<WalletButton>

View File

@ -1,17 +1,44 @@
import styled from '@emotion/styled'
import Link from 'next/link'
import React from 'react'
interface NavbarProps {}
const items = [
{
label: 'Dashboard',
href: '/dashboard',
},
{
label: 'Explore',
href: '/explore',
},
{
label: 'Leaderboard',
href: '/leaderboard',
},
{
label: 'Docs',
href: '/docs',
},
{
label: 'Auction',
href: '/auction',
},
{
label: 'Roles',
href: '/roles',
},
]
const Navbar: React.FC<NavbarProps> = () => {
return (
<Navigation>
<NavItem active>Dashboard</NavItem>
<NavItem>Explore</NavItem>
<NavItem>Leaderboard</NavItem>
<NavItem>Docs</NavItem>
<NavItem>Auction</NavItem>
<NavItem>Roles</NavItem>
{items.map((item, index) => (
<Link href={item.href} key={'navbar-item-' + index}>
<NavItem key={index}>{item.label}</NavItem>
</Link>
))}
</Navigation>
)
}
@ -40,8 +67,9 @@ const NavItem = styled.li<{ active?: boolean }>`
font-weight: 400;
font-size: 14px;
line-height: 20px;
cursor: pointer;
color: rgb(var(--lsd-text-primary));
padding: 4px 0;
cursor: pointer;
${(props) =>
props.active &&

View File

@ -0,0 +1,39 @@
import { OperatorFilter } from '@/components/Explore/OperatorFilter'
import { OperatorGrid } from '@/components/Explore/OperatorGrid'
import styled from '@emotion/styled'
import React from 'react'
interface ExploreSectionProps {}
const ExploreSection: React.FC<ExploreSectionProps> = () => {
return (
<StyledExploreSection>
<h1 className="section-title">Operators</h1>
<OperatorFilter />
<OperatorGrid />
</StyledExploreSection>
)
}
const StyledExploreSection = styled.main`
display: flex;
flex-direction: column;
overflow: hidden;
.section-title {
text-align: center;
font-size: 40px;
line-height: 48px;
margin-top: 60px;
}
@media (max-width: 991px) {
padding: 0 20px;
.section-title {
margin-top: 40px;
}
}
`
export default ExploreSection

View File

@ -0,0 +1 @@
export { default as DashboardContainer } from './ExploreContainer'

View File

@ -1,3 +1,4 @@
import { Footer } from '@/components/Footer'
import { Header } from '@/components/Header/Header'
import { breakpoints, uiConfigs } from '@/configs/ui.configs'
import styled from '@emotion/styled'
@ -12,6 +13,7 @@ const DashboardLayout: React.FC<DashboardLayoutProps> = (
<Container>
<Header />
<main>{props.children}</main>
<Footer />
</Container>
)
}

View File

@ -1,36 +1,35 @@
import { Footer } from '@/components/Footer'
import { Header } from '@/components/Header/Header'
import { breakpoints, uiConfigs } from '@/configs/ui.configs'
import styled from '@emotion/styled'
import { PropsWithChildren } from 'react'
import React, { PropsWithChildren } from 'react'
const Root = styled.div`
display: grid;
grid-template-columns: repeat(16, 1fr);
gap: 0 16px;
interface DefaultLayoutProps {}
max-width: ${uiConfigs.maxContainerWidth}px;
margin: 0 auto;
@media (max-width: ${breakpoints.md}px) {
flex-direction: column-reverse;
width: 100%;
}
`
const Main = styled.main`
width: 100%;
grid-column: 1 / 17;
margin-top: ${uiConfigs.navbarRenderedHeight}px;
margin-bottom: 148px;
@media (max-width: ${breakpoints.md}px) {
margin-top: ${uiConfigs.navbarMobileHeight}px;
}
`
export default function DefaultLayout(props: PropsWithChildren) {
const DefaultLayout: React.FC<DefaultLayoutProps> = (
props: PropsWithChildren,
) => {
return (
<Root>
<Main>{props.children}</Main>
</Root>
<Container>
<Header />
<main>{props.children}</main>
<Footer />
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
overflow: hidden;
max-width: ${uiConfigs.maxContainerWidth}px;
margin: 0 auto;
padding: 24px 0;
@media (max-width: ${breakpoints.md}px) {
padding: 0 20px;
}
`
export default DefaultLayout

View File

@ -0,0 +1,35 @@
import { Footer } from '@/components/Footer'
import { Header } from '@/components/Header/Header'
import { breakpoints, uiConfigs } from '@/configs/ui.configs'
import styled from '@emotion/styled'
import React, { PropsWithChildren } from 'react'
interface ExploreLayoutProps {}
const ExploreLayout: React.FC<ExploreLayoutProps> = (
props: PropsWithChildren,
) => {
return (
<Container>
<Header />
<main>{props.children}</main>
<Footer />
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
overflow: hidden;
max-width: ${uiConfigs.maxContainerWidth}px;
margin: 0 auto;
padding: 24px 0;
@media (max-width: ${breakpoints.md}px) {
padding: 0 20px;
}
`
export default ExploreLayout

View File

@ -0,0 +1 @@
export { default as DashboardLayout } from './Explore.layout'

18
src/pages/explore.tsx Normal file
View File

@ -0,0 +1,18 @@
import { SEO } from '@/components/SEO'
import ExploreContainer from '@/containers/Explore/ExploreContainer'
import ExploreLayout from '@/layouts/ExploreLayout/Explore.layout'
type PageProps = {}
export default function ExplorePage(props: PageProps) {
return (
<>
<SEO />
<ExploreContainer />
</>
)
}
ExplorePage.getLayout = function getLayout(page: React.ReactNode) {
return <ExploreLayout>{page}</ExploreLayout>
}