mirror of
https://github.com/acid-info/free.technology.git
synced 2025-02-20 13:38:33 +00:00
challenge data fetching
This commit is contained in:
parent
9b4886ec55
commit
e7eac9eb1b
3
.env
3
.env
@ -1 +1,2 @@
|
||||
NEXT_PUBLIC_SITE_URL=
|
||||
NEXT_PUBLIC_SITE_URL=
|
||||
NEXT_GITHUB_PERSONAL_ACCESS_TOKEN=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
|
11
README.md
11
README.md
@ -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
3
public/icons/chevron.svg
Normal 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 |
@ -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
|
||||
|
106
src/components/Footer/HomeFooter.tsx
Normal file
106
src/components/Footer/HomeFooter.tsx
Normal 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
|
@ -1 +1,2 @@
|
||||
export { default as Footer } from './Footer'
|
||||
export { default as HomeFooter } from './HomeFooter'
|
||||
|
@ -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`
|
||||
|
@ -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 />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
21
src/layouts/SubPageLayout/SubPage.layout.tsx
Normal file
21
src/layouts/SubPageLayout/SubPage.layout.tsx
Normal 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 />
|
||||
</>
|
||||
)
|
||||
}
|
1
src/layouts/SubPageLayout/index.ts
Normal file
1
src/layouts/SubPageLayout/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as SubPageLayout } from './SubPage.layout'
|
114
src/pages/api/challenges.ts
Normal file
114
src/pages/api/challenges.ts
Normal 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()
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 project’s 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 />
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user