challenge data fetching

This commit is contained in:
jinhojang6 2023-11-03 14:53:38 +09:00
parent 9b4886ec55
commit e7eac9eb1b
15 changed files with 320 additions and 53 deletions

3
.env
View File

@ -1 +1,2 @@
NEXT_PUBLIC_SITE_URL=
NEXT_PUBLIC_SITE_URL=
NEXT_GITHUB_PERSONAL_ACCESS_TOKEN=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

View File

@ -17,7 +17,16 @@ $ git clone https://github.com/acid-info/ift.git
$ yarn install
```
3. Start the development server:
3. Set .env
- Get Github Personal Access Token at [https://github.com/settings/tokens/new?scopes=repo](https://github.com/settings/tokens/new?scopes=repo)
```
NEXT_PUBLIC_SITE_URL=
NEXT_GITHUB_PERSONAL_ACCESS_TOKEN=
```
4. Start the development server:
```bash
$ yarn dev
```

3
public/icons/chevron.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.49988 8.33875L4.32238 9.16125L6.99988 6.48959L9.67738 9.16125L10.4999 8.33875L6.99988 4.83875L3.49988 8.33875Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 242 B

View File

@ -18,28 +18,10 @@ export const Footer = () => {
const Container = styled.nav`
display: flex;
padding-top: 16px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid rgba(0, 0, 0, 0.28);
margin-top: 140px;
font-size: 18px;
font-style: normal;
font-weight: 400;
line-height: 130%;
div {
width: 50%;
}
span {
display: flex;
align-items: center;
justify-content: space-between;
width: 50%;
}
flex-direction: column;
background-color: black;
color: white;
padding: 16px;
`
export default Footer

View File

@ -0,0 +1,106 @@
import styled from '@emotion/styled'
import Image from 'next/image'
export const HomeFooter = () => {
const handleGoToTop = () => {
window.scrollTo(0, 0)
}
return (
<Container>
<Header>
<GoToTop onClick={handleGoToTop}>
<Image
src="/icons/chevron.svg"
width={14}
height={14}
alt="go to top"
/>
</GoToTop>
</Header>
<h2>Get In Touch</h2>
<Description>
<p>
Share our mission and want to work with us?
<br />
Contact the IFT to see what we can do for you and your startup.
</p>
<button>Email us</button>
</Description>
<div>
<p>
Institute of Free Technology.
<br />©{new Date().getFullYear()}
</p>
</div>
<span>
<p>LinkedIn</p>
<p>Vimeo</p>
<p>Youtube</p>
</span>
</Container>
)
}
const Container = styled.nav`
display: flex;
box-sizing: border-box;
background-color: black;
flex-direction: column;
color: white;
width: 50vw;
padding: 16px;
margin-top: 204px;
h2 {
padding-block: 24px;
font-size: 34px;
font-weight: 400;
line-height: 130%;
}
`
const Header = styled.div`
padding-bottom: 16px;
margin-bottom: 16px;
border-bottom: 1px solid rgba(255, 255, 255, 0.18);
`
const GoToTop = styled.button`
display: flex;
width: 24px;
height: 24px;
justify-content: center;
align-items: center;
border: 1px solid #fff;
background-color: transparent;
cursor: pointer;
`
const Description = styled.div`
display: flex;
flex-direction: column;
gap: 40px;
margin-top: 32px;
margin-bottom: 64px;
p {
font-size: 36px;
line-height: 122%;
}
button {
display: flex;
width: 150px;
padding: 10px 34px;
justify-content: center;
align-items: center;
gap: 8px;
background-color: transparent;
border: 1px solid #fff;
color: #fff;
cursor: pointer;
}
`
export default HomeFooter

View File

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

View File

@ -92,8 +92,10 @@ const Header = styled.div`
padding-block: 24px;
`
const Toggle = styled.div`
const Toggle = styled.button`
cursor: pointer;
background-color: transparent;
border: none;
`
const Title = styled.div`

View File

@ -1,7 +1,5 @@
import { Footer } from '@/components/Footer'
import { Main } from '@/components/Main'
import { Navbar } from '@/components/Navbar'
import { SEO } from '@/components/SEO'
import { PropsWithChildren } from 'react'
import { MainProps } from '../../components/Main/Main'
@ -14,10 +12,8 @@ export default function DefaultLayout(props: PropsWithChildren<Props>) {
return (
<>
<SEO />
<Navbar />
<Main {...mainProps}>{props.children}</Main>
<Footer />
</>
)
}

View File

@ -0,0 +1,21 @@
import { Footer } from '@/components/Footer'
import { Main } from '@/components/Main'
import { Navbar } from '@/components/Navbar'
import { PropsWithChildren } from 'react'
import { MainProps } from '../../components/Main/Main'
interface Props {
mainProps?: Partial<MainProps>
}
export default function SubPageLayout(props: PropsWithChildren<Props>) {
const { mainProps = {} } = props
return (
<>
<Navbar />
<Main {...mainProps}>{props.children}</Main>
<Footer />
</>
)
}

View File

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

114
src/pages/api/challenges.ts Normal file
View File

@ -0,0 +1,114 @@
// pages/api/issues.js
import https from 'https'
import { NextApiRequest, NextApiResponse } from 'next'
export default function handler(
req: NextApiRequest,
res: NextApiResponse<any>,
) {
if (req.method !== 'POST') {
res.setHeader('Allow', ['POST'])
return res.status(405).end(`Method ${req.method} Not Allowed`)
}
const { owner, repo } = req.body
if (!owner || !repo) {
return res.status(400).json({ error: 'Owner and repo are required.' })
}
const query = `
{
repository(owner: "${owner}", name: "${repo}") {
issues(first: 100, states: OPEN) {
nodes {
id
title
url
author {
login
avatarUrl
}
labels(first: 10) {
nodes {
name
}
}
commentCount: comments {
totalCount
}
commentsDetailed: comments(first: 10) {
nodes {
id
author {
login
}
body
createdAt
}
}
assignees(first: 10) {
nodes {
login
avatarUrl
}
}
milestone {
title
}
createdAt
updatedAt
projectCards(first: 10) {
nodes {
project {
name
url
}
}
}
}
}
}
}
`
const options = {
hostname: 'api.github.com',
path: '/graphql',
method: 'POST',
headers: {
'User-Agent': 'NodeJS-Script',
Authorization: `bearer ${process.env.NEXT_GITHUB_PERSONAL_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
}
const graphqlReq = https.request(options, (graphqlRes) => {
let data = ''
graphqlRes.on('data', (chunk) => {
data += chunk
})
graphqlRes.on('end', () => {
try {
const response = JSON.parse(data)
const rawIssues = response.data.repository.issues.nodes
// console.log('rawIssues', rawIssues)
res.status(200).json(rawIssues)
} catch (error) {
console.error('Failed to parse response:', error)
res.status(500).json({ error: 'Failed to parse response' })
}
})
})
graphqlReq.on('error', (error) => {
console.error(`Got an error: ${error.message}`)
res.status(500).json({ error: error.message })
})
graphqlReq.write(JSON.stringify({ query }))
graphqlReq.end()
}

View File

@ -1,37 +1,58 @@
import { SEO } from '@/components/SEO'
import { getJobs } from '../../utils/getJobs'
import { DefaultLayout } from '../layouts/DefaultLayout'
import { SubPageLayout } from '../layouts/SubPageLayout'
const Page = () => {
const Page = ({ issues }: any) => {
return (
<>
<SEO />
<div></div>
<div>
<h1>Open Issues</h1>
<ul>
{issues.map((issue: any) => (
<li key={issue.id}>
<a href={issue.url}>{issue.title}</a>
</li>
))}
</ul>
</div>
</>
)
}
Page.getLayout = function getLayout(page: React.ReactNode) {
return <DefaultLayout>{page}</DefaultLayout>
return <SubPageLayout>{page}</SubPageLayout>
}
export async function getStaticProps() {
try {
const jobs = await getJobs(['all'], '')
let issues = []
return {
props: {
jobs,
try {
const res = await fetch('http://localhost:3000/api/challenges', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
revalidate: 3600, // In seconds
body: JSON.stringify({
owner: 'waku-org',
repo: 'bounties',
}),
})
if (!res.ok) {
throw new Error(`Failed to fetch issues, status: ${res.status}`)
}
const data = await res.json()
issues = data
} catch (error) {
return {
props: {
jobs: [],
},
revalidate: 3600,
}
console.error('Error fetching issues:', error)
}
return {
props: {
issues,
},
revalidate: 3600, // In seconds. Adjust to your needs.
}
}

View File

@ -8,7 +8,9 @@ import { Section } from '@/components/Section'
import { Navbar } from '@/components/Navbar'
import { Team, Member } from '@/components/Team'
import { Mission } from '@/components/Mission'
import { Footer } from '@/components/Footer'
import { HomeFooter } from '@/components/Footer'
<SEO />
<Home>
<Hero>
@ -115,8 +117,8 @@ import { Footer } from '@/components/Footer'
</PortfolioItem>
</Portfolio>
<Mission title="Mission">
## We support the development, adoption, and accessibility of solutions to digital age problems and are guided by our principles: liberty, censorship resistance, security, privacy, and inclusivity.
## We support the development, adoption, and accessibility of solutions to digital age problems and are guided by our principles: liberty, censorship resistance, security, privacy, and inclusivity.
<div>
<p>We seek to connect with and support those innovating to defend our digital rights. The IFT emerged from Status, which Jarrad Hope and Carl Bennetts founded to enable the free flow of information and protect the right to private, secure conversations in 2017.</p>
<p>Over time, gaps in the infrastructure required to deliver Status in a form that fully reflects its founders, contributors, and users values became apparent, and the projects scope widened. The IFT and the startups it incubates are the product of this expanded scope.</p>
@ -149,5 +151,6 @@ import { Footer } from '@/components/Footer'
Partner & Portfolio Manager
</Member>
</Team>
</Home>
<HomeFooter />

View File

@ -1,14 +1,16 @@
import { Box } from '@/components/Box'
import { JobFilter, JobList } from '@/components/JobList'
import { SEO } from '@/components/SEO'
import { SubPageLayout } from '@/layouts/SubPageLayout'
import { useState } from 'react'
import { getJobs } from '../../utils/getJobs'
import { DefaultLayout } from '../layouts/DefaultLayout'
const Page = ({ jobs }: any) => {
const [activeBUs, setActiveBUs] = useState<string[]>([])
return (
<>
<SEO />
<div>
<Box>
<JobFilter
@ -26,7 +28,7 @@ const Page = ({ jobs }: any) => {
}
Page.getLayout = function getLayout(page: React.ReactNode) {
return <DefaultLayout>{page}</DefaultLayout>
return <SubPageLayout>{page}</SubPageLayout>
}
export async function getStaticProps() {

View File

@ -1,7 +1,12 @@
import { SEO } from '@/components/SEO'
import { DefaultLayout } from '../layouts/DefaultLayout'
const Page = () => {
return <></>
return (
<>
<SEO />
</>
)
}
Page.getLayout = function getLayout(page: React.ReactNode) {