feat: allow editing navbar links for each fund

This commit is contained in:
Artur
2024-10-16 20:26:08 -03:00
parent 6f5cbe08b1
commit efe3885b66
10 changed files with 45 additions and 481 deletions

View File

@@ -65,7 +65,7 @@ const Header = () => {
<div className="flex gap-2 items-center text-base leading-5">
{!!fund &&
fundHeaderNavLinks.map((link) => (
fundHeaderNavLinks[fund.slug].map((link) => (
<CustomLink
key={link.title}
href={`/${fundSlug}/${link.href}`}

View File

@@ -70,7 +70,7 @@ const MobileNav = () => {
</div>
<nav className="fixed mt-8 h-full">
{!!fund &&
fundHeaderNavLinks.map((link) => (
fundHeaderNavLinks[fund.slug].map((link) => (
<div key={link.title} className="px-12 py-4">
<CustomLink
href={`/${fundSlug}/${link.href}`}

View File

@@ -1,5 +1,18 @@
export const fundHeaderNavLinks = [
{ title: 'Apply', href: 'apply', isButton: false },
{ title: 'FAQs', href: 'faq', isButton: false },
{ title: 'About', href: 'about', isButton: false },
]
import { FundSlug } from '@prisma/client'
export const fundHeaderNavLinks: Record<
FundSlug,
{ title: string; href: string; isButton: boolean }[]
> = {
monero: [
{ title: 'Apply', href: 'apply', isButton: false },
{ title: 'FAQs', href: 'faq', isButton: false },
{ title: 'About', href: 'about', isButton: false },
],
firo: [{ title: 'About', href: 'about', isButton: false }],
privacyguides: [{ title: 'About', href: 'about', isButton: false }],
general: [
{ title: 'FAQs', href: 'faq', isButton: false },
{ title: 'About', href: 'about', isButton: false },
],
}

View File

@@ -25,7 +25,7 @@ The MAGIC Monero Fund accepts applications on a rolling basis. Applications will
## Proposal Format
Applicants must submit their applications on the web form [here](/apply). Alternatively, applicants can submit a PDF file by email to MoneroFund@magicgrants.org . Applicants are free to use their legal name or a pseudonym at this step, although note the "Eligibility" section below.
Applicants must submit their applications on the web form [here](/monero/apply). Alternatively, applicants can submit a PDF file by email to MoneroFund@magicgrants.org . Applicants are free to use their legal name or a pseudonym at this step, although note the "Eligibility" section below.
The research plan should be 3-5 pages (not counting citations, any images, and biographical sketch) and include the following:
@@ -73,7 +73,7 @@ If your proposal seek to uncover weaknesses in the privacy and/or security featu
## How to Submit an Application
Applicants must submit their applications on the web form [here](/apply). Alternatively, applicants can submit a PDF file by email to MoneroFund@magicgrants.org
Applicants must submit their applications on the web form [here](/monero/apply). Alternatively, applicants can submit a PDF file by email to MoneroFund@magicgrants.org
## Contact

View File

@@ -2,7 +2,7 @@ import xss from 'xss'
import { FundSlug } from '@prisma/client'
import markdownToHtml from '../../utils/markdownToHtml'
import { getSingleFile } from '../../utils/md'
import { fileExists, getSingleFile } from '../../utils/md'
import { fundSlugs } from '../../utils/funds'
export default function About({ content }: { content: string }) {
@@ -28,7 +28,9 @@ export async function getStaticProps({ params }: { params: { fund: FundSlug } })
export function getStaticPaths() {
return {
paths: fundSlugs.map((fund) => `/${fund}/about`),
paths: fundSlugs
.filter((fundSlug) => fileExists(`docs/${fundSlug}/about.md`))
.map((fundSlug) => `/${fundSlug}/about`),
fallback: true,
}
}

View File

@@ -1,8 +1,9 @@
import { FundSlug } from '@prisma/client'
import markdownToHtml from '../../utils/markdownToHtml'
import { getSingleFile } from '../../utils/md'
import { fileExists, getSingleFile } from '../../utils/md'
import BigDumbMarkdown from '../../components/BigDumbMarkdown'
import { fundSlugs } from '../../utils/funds'
import { fundHeaderNavLinks } from '../../data/headerNavLinks'
export default function About({ content }: { content: string }) {
return <BigDumbMarkdown content={content} />
@@ -22,7 +23,9 @@ export async function getStaticProps({ params }: { params: { fund: FundSlug } })
export function getStaticPaths() {
return {
paths: fundSlugs.map((fund) => `/${fund}/apply_research`),
paths: fundSlugs
.filter((fundSlug) => fileExists(`docs/${fundSlug}/apply_research.md`))
.map((fundSlug) => `/${fundSlug}/apply_research`),
fallback: true,
}
}

View File

@@ -2,7 +2,7 @@ import xss from 'xss'
import { FundSlug } from '@prisma/client'
import markdownToHtml from '../../utils/markdownToHtml'
import { getSingleFile } from '../../utils/md'
import { fileExists, getSingleFile } from '../../utils/md'
import { fundSlugs } from '../../utils/funds'
export default function Faq({ content }: { content: string }) {
@@ -28,7 +28,9 @@ export async function getStaticProps({ params }: { params: { fund: FundSlug } })
export function getStaticPaths() {
return {
paths: fundSlugs.map((fund) => `/${fund}/faq`),
paths: fundSlugs
.filter((fundSlug) => fileExists(`docs/${fundSlug}/faq.md`))
.map((fundSlug) => `/${fundSlug}/faq`),
fallback: true,
}
}

View File

@@ -1,235 +0,0 @@
import { useRouter } from 'next/router'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import Link from 'next/link'
import { Button } from '../../components/ui/button'
import { trpc } from '../../utils/trpc'
import { useFundSlug } from '../../utils/use-fund-slug'
import { useToast } from '../../components/ui/use-toast'
import { fundSlugToRecipientEmail } from '../../utils/funds'
export default function Apply() {
const fundSlug = useFundSlug()
const router = useRouter()
const { toast } = useToast()
const applyMutation = trpc.application.submitApplication.useMutation()
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
async function onSubmit(data: Record<string, string>) {
if (!fundSlug) return
await applyMutation.mutateAsync({ fundSlug, formData: data })
toast({ title: 'Success', description: 'Application successfully submitted!' })
router.push(`/${fundSlug}/`)
}
if (!fundSlug) return <></>
return (
<div className="mx-auto flex-1 flex flex-col items-center justify-center gap-4 py-8 prose">
<form onSubmit={handleSubmit(onSubmit)} className="max-w-5xl flex flex-col gap-4 p-4">
<div>
<h1>Application for Firo Fund Project Listing or General Fund Grant</h1>
<p>Thanks for your interest in the Firo Fund!</p>
<p>
We&#39;re incredibly grateful to contributors like you working to support Firo and other
free and open source projects.
</p>
<p>
Please fill in your contact information and your project idea below so that we can process
your inquiry.
</p>
</div>
<label className="checkbox">
<input type="checkbox" {...register('general_fund')} />
Apply to receive a grant from the MAGIC Firo Fund.
</label>
<label className="checkbox">
<input type="checkbox" {...register('explore_page')} />
Apply for project to be listed on the MAGIC Firo Fund donation page.
</label>
<div className="w-full flex flex-col">
<label htmlFor="project_name">Project Name *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="project_name"
type="text"
{...register('project_name', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="your_name">Your Name or Pseudonym *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="your_name"
type="text"
{...register('your_name', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="email">Email *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="email"
type="text"
{...register('email', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="github">Project GitHub (if applicable)</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="github"
type="text"
{...register('github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="personal_github">Personal GitHub (if applicable)</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="personal_github"
type="text"
{...register('personal_github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="other_contact">Other Contact Details (if applicable)</label>
<small>
Please list any other relevant contact details you are comfortable sharing in case we
need to reach out with questions. These could include GitHub username, Twitter username,
LinkedIn, Reddit handle, other social media handles, emails, phone numbers, usernames,
etc.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_contact"
{...register('other_contact')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="short_description">Short Project Description *</label>
<small>
This will be listed on the explore projects page of the Firo Fund website. 2-3
sentences.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="short_description"
{...register('short_description', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="long_description">Long Project Description</label>
<small>
This will be listed on your personal project page of the Firo Fund website. It can be
longer and go into detail about your project.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="long_description"
{...register('long_description')}
/>
</div>
<label className="checkbox">
<input type="checkbox" {...register('free_open_source')} />
Is the project free and open source?
</label>
<label className="checkbox">
<input type="checkbox" {...register('are_you_lead')} />
Are you the Project Lead / Lead Contributor
</label>
<div className="w-full flex flex-col">
<label htmlFor="other_lead">
If someone else, please list the project&#39;s Lead Contributor or Maintainer
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_lead"
type="text"
{...register('other_lead')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="potential_impact">Potential Impact *</label>
<small>Why is this project important to the Firo community?</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="potential_impact"
{...register('potential_impact', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="timelines">Project Timelines and Potential Milestones *</label>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="timelines"
{...register('timelines', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="proposed_budget">
If you&#39;re applying for a grant from the general fund, please submit a proposed
budget for the requested amount and how it will be used.
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="proposed_budget"
type="text"
{...register('proposed_budget')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="bios">Applicant Bios (Optional)</label>
<small>List relevant accomplishments.</small>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="bios"
type="text"
{...register('bios')}
/>
</div>
<small>
The MAGIC Firo Fund may require each recipient to sign a Grant Agreement before any funds
are disbursed. This agreement will set milestones and funds will only be released upon
completion of milestones. In order to comply with US regulations, recipients will need to
identify themselves to MAGIC Grants, in accordance with US law.
</small>
<Button disabled={applyMutation.isPending}>Apply</Button>
<p>
After submitting your application, please allow our team up to three weeks to review your
application. Email us at{' '}
<a href={`mailto:${fundSlugToRecipientEmail[fundSlug]}`}>
{fundSlugToRecipientEmail[fundSlug]}
</a>{' '}
if you have any questions.
</p>
</form>
</div>
)
}

View File

@@ -1,231 +0,0 @@
import { useRouter } from 'next/router'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import Link from 'next/link'
import { Button } from '../../components/ui/button'
import { trpc } from '../../utils/trpc'
import { useFundSlug } from '../../utils/use-fund-slug'
import { useToast } from '../../components/ui/use-toast'
import { fundSlugToRecipientEmail } from '../../utils/funds'
export default function Apply() {
const fundSlug = useFundSlug()
const router = useRouter()
const { toast } = useToast()
const applyMutation = trpc.application.submitApplication.useMutation()
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
async function onSubmit(data: Record<string, string>) {
if (!fundSlug) return
await applyMutation.mutateAsync({ fundSlug, formData: data })
toast({ title: 'Success', description: 'Application successfully submitted!' })
router.push(`/${fundSlug}/`)
}
if (!fundSlug) return <></>
return (
<div className="mx-auto flex-1 flex flex-col items-center justify-center gap-4 py-8 prose">
<form onSubmit={handleSubmit(onSubmit)} className="max-w-5xl flex flex-col gap-4 p-4">
<div>
<h1>Application for General Fund Project Listing or General Fund Grant</h1>
<p>Thanks for your interest in a MAGIC Grants project!</p>
<p>
The MAGIC Grants is always interested in supporting various fundraising efforts for
qualifying projects and activities.
</p>
</div>
<label className="checkbox">
<input type="checkbox" {...register('general_fund')} />
Apply to receive a grant from MAGIC Grants.
</label>
<label className="checkbox">
<input type="checkbox" {...register('explore_page')} />
Apply for project to be listed on the General Fund donation page.
</label>
<div className="w-full flex flex-col">
<label htmlFor="project_name">Project Name *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="project_name"
type="text"
{...register('project_name', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="your_name">Your Name or Pseudonym *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="your_name"
type="text"
{...register('your_name', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="email">Email *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="email"
type="text"
{...register('email', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="github">Project GitHub (if applicable)</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="github"
type="text"
{...register('github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="personal_github">Personal GitHub (if applicable)</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="personal_github"
type="text"
{...register('personal_github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="other_contact">Other Contact Details (if applicable)</label>
<small>
Please list any other relevant contact details you are comfortable sharing in case we
need to reach out with questions. These could include GitHub username, Twitter username,
LinkedIn, Reddit handle, other social media handles, emails, phone numbers, usernames,
etc.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_contact"
{...register('other_contact')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="short_description">Short Project Description *</label>
<small>
This will be listed on the explore projects page of the General Fund website. 2-3
sentences.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="short_description"
{...register('short_description', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="long_description">Long Project Description</label>
<small>
This will be listed on your personal project page of the General Fund website. It can be
longer and go into detail about your project.
</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="long_description"
{...register('long_description')}
/>
</div>
<label className="checkbox">
<input type="checkbox" {...register('free_open_source')} />
Is the project free and open source?
</label>
<label className="checkbox">
<input type="checkbox" {...register('are_you_lead')} />
Are you the Project Lead / Lead Contributor
</label>
<div className="w-full flex flex-col">
<label htmlFor="other_lead">
If someone else, please list the project&#39;s Lead Contributor or Maintainer
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_lead"
type="text"
{...register('other_lead')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="potential_impact">Potential Impact *</label>
<small>Why is this project important to the cryptocurrency and/or privacy communities?</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="potential_impact"
{...register('potential_impact', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="timelines">Project Timelines and Potential Milestones *</label>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="timelines"
{...register('timelines', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="proposed_budget">
If you&#39;re applying for a grant from MAGIC Grants, please submit a proposed
budget for the requested amount and how it will be used.
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="proposed_budget"
type="text"
{...register('proposed_budget')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="bios">Applicant Bios (Optional)</label>
<small>List relevant accomplishments.</small>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="bios"
type="text"
{...register('bios')}
/>
</div>
<small>
MAGIC Grants may require each recipient to sign a Grant Agreement before any
funds are disbursed. This agreement will set milestones and funds will only be released
upon completion of milestones. In order to comply with US regulations, recipients will
need to identify themselves to MAGIC Grants, in accordance with US law.
</small>
<Button disabled={applyMutation.isPending}>Apply</Button>
<p>
After submitting your application, please allow our team up to three weeks to review your
application. Email us at{' '}
<a href={`mailto:${fundSlugToRecipientEmail[fundSlug]}`}>
{fundSlugToRecipientEmail[fundSlug]}
</a>{' '}
if you have any questions.
</p>
</form>
</div>
)
}

View File

@@ -28,6 +28,16 @@ export function getSingleFile(path: string) {
return fs.readFileSync(fullPath, 'utf8')
}
export function fileExists(path: string) {
const fullPath = join(process.cwd(), path)
try {
fs.accessSync(fullPath, fs.constants.F_OK)
return true
} catch {
return false
}
}
export function getProjectBySlug(slug: string, fundSlug: FundSlug) {
const realSlug = slug.replace(/\.md$/, '')
const fullPath = join(directories[fundSlug], `${sanitize(realSlug)}.md`)