mirror of
https://github.com/acid-info/logos-ordinals-dashboard.git
synced 2025-02-06 02:23:27 +00:00
feat: implement explore page
This commit is contained in:
parent
e42c224b86
commit
b896eca534
81
src/components/Explore/OperatorFilter/OperatorFilter.tsx
Normal file
81
src/components/Explore/OperatorFilter/OperatorFilter.tsx
Normal 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
|
1
src/components/Explore/OperatorFilter/index.ts
Normal file
1
src/components/Explore/OperatorFilter/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as OperatorFilter } from './OperatorFilter'
|
105
src/components/Explore/OperatorGrid/OperatorGrid.tsx
Normal file
105
src/components/Explore/OperatorGrid/OperatorGrid.tsx
Normal 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
|
1
src/components/Explore/OperatorGrid/index.ts
Normal file
1
src/components/Explore/OperatorGrid/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as OperatorGrid } from './OperatorGrid'
|
55
src/components/Footer/Footer.tsx
Normal file
55
src/components/Footer/Footer.tsx
Normal 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
|
1
src/components/Footer/index.ts
Normal file
1
src/components/Footer/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Footer } from './Footer'
|
@ -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>
|
||||
|
@ -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 &&
|
||||
|
39
src/containers/Explore/ExploreContainer.tsx
Normal file
39
src/containers/Explore/ExploreContainer.tsx
Normal 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
|
1
src/containers/Explore/index.ts
Normal file
1
src/containers/Explore/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as DashboardContainer } from './ExploreContainer'
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
35
src/layouts/ExploreLayout/Explore.layout.tsx
Normal file
35
src/layouts/ExploreLayout/Explore.layout.tsx
Normal 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
|
1
src/layouts/ExploreLayout/index.ts
Normal file
1
src/layouts/ExploreLayout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as DashboardLayout } from './Explore.layout'
|
18
src/pages/explore.tsx
Normal file
18
src/pages/explore.tsx
Normal 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>
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user