Apply OpenSats UI enhancements
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
"extends": "next/core-web-vitals",
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off"
|
||||
}
|
||||
}
|
||||
|
||||
27
components/CustomLink.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/* eslint-disable jsx-a11y/anchor-has-content */
|
||||
import Link from 'next/link'
|
||||
import { AnchorHTMLAttributes, DetailedHTMLProps } from 'react'
|
||||
|
||||
const CustomLink = ({
|
||||
href,
|
||||
...rest
|
||||
}: DetailedHTMLProps<
|
||||
AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||
HTMLAnchorElement
|
||||
>) => {
|
||||
const isInternalLink = href && href.startsWith('/')
|
||||
const isAnchorLink = href && href.startsWith('#')
|
||||
|
||||
if (isInternalLink) {
|
||||
// @ts-ignore
|
||||
return <Link href={href} {...rest} />
|
||||
}
|
||||
|
||||
if (isAnchorLink) {
|
||||
return <a href={href} {...rest} />
|
||||
}
|
||||
|
||||
return <a target="_blank" rel="noopener noreferrer" href={href} {...rest} />
|
||||
}
|
||||
|
||||
export default CustomLink
|
||||
@@ -58,7 +58,7 @@ const DonationSteps: React.FC<DonationStepsProps> = ({
|
||||
const payload = {
|
||||
amount,
|
||||
project_slug: projectSlug,
|
||||
project_name: projectNamePretty
|
||||
project_name: projectNamePretty,
|
||||
}
|
||||
|
||||
if (email) {
|
||||
@@ -218,7 +218,9 @@ const DonationSteps: React.FC<DonationStepsProps> = ({
|
||||
className="color-me-monero h-8 w-8"
|
||||
/>
|
||||
)}
|
||||
<span className="whitespace-nowrap">Donate with Monero or Bitcoin</span>
|
||||
<span className="whitespace-nowrap">
|
||||
Donate with Monero or Bitcoin
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
name="stripe"
|
||||
|
||||
23
components/Footer.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import CustomLink from './CustomLink'
|
||||
|
||||
// import SocialIcon from '@/components/social-icons'
|
||||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer>
|
||||
<div className="mb-4 mt-16 flex flex-col items-center">
|
||||
<div className="space-x-4 text-center text-xs text-gray-500 dark:text-gray-400">
|
||||
MAGIC Grants is a 501(c)(3) non-profit organization. All gifts and
|
||||
donations are tax-deductible to the full extent of the law.
|
||||
</div>
|
||||
<div className="mb-2 flex space-x-2 text-xs text-gray-500 dark:text-gray-400"></div>
|
||||
<div className="space-x-4 text-center text-xs text-gray-500 dark:text-gray-400">
|
||||
© {new Date().getFullYear()} MAGIC Grants. This website builds upon
|
||||
technology by Open Sats.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -1,29 +1,41 @@
|
||||
import Nav from 'react-bootstrap/Nav'
|
||||
import Navbar from 'react-bootstrap/Navbar'
|
||||
import Container from 'react-bootstrap/Container'
|
||||
// import NavDropdown from 'react-bootstrap/NavDropdown'
|
||||
// import Row from 'react-bootstrap/Row'
|
||||
// import Col from 'react-bootstrap/Col'
|
||||
// import Image from 'next/image'
|
||||
// import Link from 'next/link'
|
||||
// import samplelogo from '/public/favicon.png'
|
||||
import Link from './CustomLink'
|
||||
import MobileNav from './MobileNav'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
import headerNavLinks from '../data/headerNavLinks'
|
||||
import Logo from './Logo'
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<header className="pb-10 sm:pb-10">
|
||||
<Navbar collapseOnSelect expand="lg" variant="light" className="color-nav navbar-expand-sm px-2 sm:px-4 py-2.5 fixed w-full z-20 top-0 left-0">
|
||||
<Container>
|
||||
<Navbar.Brand href="/">MAGIC Monero Fund</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
|
||||
<Navbar.Collapse id="responsive-navbar-nav">
|
||||
<Nav className="me-auto">
|
||||
<Nav.Link href="/apply">Apply</Nav.Link>
|
||||
<Nav.Link href="/faq">FAQs</Nav.Link>
|
||||
<Nav.Link href="/about">About Us</Nav.Link>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Container>
|
||||
</Navbar>
|
||||
<header className="flex items-center justify-between py-10">
|
||||
<div>
|
||||
<Link
|
||||
href="/"
|
||||
aria-label="Monero Fund"
|
||||
className="flex items-center mr-3 gap-4"
|
||||
>
|
||||
<Logo className="w-12 h-12" />
|
||||
<span className="text-lg font-bold">MAGIC Monero Fund</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center text-base leading-5">
|
||||
<div className="block">
|
||||
{headerNavLinks.map((link) => (
|
||||
<Link
|
||||
key={link.title}
|
||||
href={link.href}
|
||||
className={
|
||||
link.isButton
|
||||
? 'rounded border border-orange-500 bg-transparent px-4 py-2 font-semibold text-orange-500 hover:border-transparent hover:bg-orange-500 hover:text-white'
|
||||
: 'hidden p-1 font-medium text-gray-900 dark:text-gray-100 sm:p-4 md:inline-block'
|
||||
}
|
||||
>
|
||||
{link.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<ThemeSwitch />
|
||||
<MobileNav />
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,41 +1,30 @@
|
||||
import Navbar from './Header'
|
||||
import React from 'react'
|
||||
import Head from 'next/head'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faBug } from '@fortawesome/free-solid-svg-icons'
|
||||
import Link from 'next/link'
|
||||
import { ReactNode } from 'react'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const Layout: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
import SectionContainer from './SectionContainer'
|
||||
import Footer from './Footer'
|
||||
import Header from './Header'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
const LayoutWrapper = ({ children }: Props) => {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<Head>
|
||||
<title>MAGIC Monero Fund</title>
|
||||
<meta name="description" content="TKTK" />
|
||||
<link rel="icon" href="https://monerofund.org/favicon.ico" />
|
||||
|
||||
{/* Twitter */}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:creator" content="@MagicGrants" />
|
||||
<meta name="twitter:site" content="@MagicGrants" />
|
||||
<meta name="twitter:title" content="MAGIC Monero Fund" />
|
||||
<meta name="twitter:image" content="https://monerofund.org/img/crystalball.jpg" />
|
||||
|
||||
{/* Open Graph */}
|
||||
<meta property="og:url" content="https://monerofund.org/" key="ogurl" />
|
||||
<meta property="og:image" content="https://monerofund.org/img/crystalball.jpg" key="ogimage" />
|
||||
<meta property="og:site_name" content="MAGIC Monero Fund" key="ogsitename" />
|
||||
<meta property="og:title" content="MAGIC Monero Fund" key="ogtitle" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:description" content="Support the MAGIC Monero Fund and open source software and research for the Monero Project." key="ogdesc" />
|
||||
</Head>
|
||||
<Navbar />
|
||||
<main className="flex-1 flex flex-col">{ children }</main>
|
||||
<footer className="flex justify-between p-4 md:p-8 bg-light">
|
||||
<strong>© MAGIC Grants. This website builds upon technology by Open Sats.</strong>
|
||||
</footer>
|
||||
</div>
|
||||
<SectionContainer>
|
||||
<div
|
||||
className={`${inter.className} flex h-screen flex-col justify-between font-sans`}
|
||||
>
|
||||
<Header />
|
||||
<main className="mb-auto">{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
export default LayoutWrapper
|
||||
|
||||
127
components/Logo.tsx
Normal file
725
components/LogoCrystal.tsx
Normal file
84
components/MobileNav.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import CustomLink from './CustomLink'
|
||||
import headerNavLinks from '../data/headerNavLinks'
|
||||
|
||||
const MobileNav = () => {
|
||||
const [navShow, setNavShow] = useState(false)
|
||||
|
||||
const onToggleNav = () => {
|
||||
setNavShow((status) => {
|
||||
if (status) {
|
||||
document.body.style.overflow = 'auto'
|
||||
} else {
|
||||
// Prevent scrolling
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
return !status
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="md:hidden">
|
||||
<button
|
||||
className="ml-1 mr-1 h-8 w-8 rounded py-1"
|
||||
aria-label="Toggle Menu"
|
||||
onClick={onToggleNav}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="h-8 w-8 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<div
|
||||
className={`fixed left-0 top-0 z-10 h-full w-full transform bg-gray-200 opacity-95 duration-300 ease-in-out dark:bg-gray-800 ${
|
||||
navShow ? 'translate-x-0' : 'translate-x-full'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
className="mr-5 mt-11 h-8 w-8 rounded"
|
||||
aria-label="Toggle Menu"
|
||||
onClick={onToggleNav}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="h-8 w-8 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<nav className="fixed mt-8 h-full">
|
||||
{headerNavLinks.map((link) => (
|
||||
<div key={link.title} className="px-12 py-4">
|
||||
<CustomLink
|
||||
href={link.href}
|
||||
className="text-2xl font-bold tracking-tight text-gray-900 dark:text-gray-100"
|
||||
onClick={onToggleNav}
|
||||
>
|
||||
{link.title}
|
||||
</CustomLink>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MobileNav
|
||||
61
components/PageHeading.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { ReactNode } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { ProjectItem } from '../utils/types'
|
||||
import CustomLink from './CustomLink'
|
||||
import Image from 'next/image'
|
||||
import SocialIcon from './social-icons'
|
||||
|
||||
interface Props {
|
||||
project: ProjectItem
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function PageHeading({ project, children }: Props) {
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="items-start space-y-2 pb-8 pt-6 md:space-y-5 xl:grid xl:grid-cols-3 xl:gap-x-8">
|
||||
<Image
|
||||
src={project.coverImage}
|
||||
alt="avatar"
|
||||
width={300}
|
||||
height={300}
|
||||
className="h-60 w-60 mx-auto my-auto object-contain row-span-3 hidden xl:block"
|
||||
/>
|
||||
|
||||
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14 xl:col-span-2">
|
||||
{project.title}
|
||||
</h1>
|
||||
|
||||
<p>{project.summary}</p>
|
||||
|
||||
<div></div>
|
||||
|
||||
<div className="flex space-x-3 items-center">
|
||||
<p>
|
||||
by{' '}
|
||||
<a className="text-orange-500" href={project.personalWebsite}>
|
||||
{project.nym}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<SocialIcon kind="website" href={project.website} />
|
||||
{project.twitter && (
|
||||
<SocialIcon
|
||||
kind="twitter"
|
||||
href={`https://twitter.com/${project.twitter}`}
|
||||
/>
|
||||
)}
|
||||
<SocialIcon kind="github" href={project.git} />
|
||||
{/* {nostr && (
|
||||
<SocialIcon kind="nostr" href={`https://njump.me/${nostr}`} />
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="items-start space-y-2 xl:grid xl:grid-cols-3 xl:gap-x-8 xl:space-y-0">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import ReactModal from 'react-modal'
|
||||
import Image from 'next/image'
|
||||
import waffledog from '../public/waffledog.jpg'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faClose } from '@fortawesome/free-solid-svg-icons'
|
||||
import DonationForm from './DonationForm'
|
||||
@@ -11,6 +10,7 @@ type ModalProps = {
|
||||
onRequestClose: () => void
|
||||
project: ProjectItem | undefined
|
||||
}
|
||||
|
||||
const PaymentModal: React.FC<ModalProps> = ({
|
||||
isOpen,
|
||||
onRequestClose,
|
||||
@@ -25,7 +25,7 @@ const PaymentModal: React.FC<ModalProps> = ({
|
||||
<ReactModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={onRequestClose}
|
||||
className="p-16 sm:p-8 bg-white shadow-xl overflow-y-auto max-h-full sm:rounded-xl w-full sm:m-8"
|
||||
className="max-h-full max-w-4xl w-full overflow-y-auto bg-white p-8 shadow-xl dark:bg-stone-800 sm:m-8 sm:rounded-xl"
|
||||
overlayClassName="inset-0 fixed bg-[rgba(0,_0,_0,_0.75)] flex items-center justify-center"
|
||||
appElement={
|
||||
typeof window === 'undefined'
|
||||
@@ -33,15 +33,15 @@ const PaymentModal: React.FC<ModalProps> = ({
|
||||
: document?.getElementById('root') || undefined
|
||||
}
|
||||
>
|
||||
<div className="flex justify-end relative -mb-12">
|
||||
<div className="relative -mb-12 flex justify-end">
|
||||
<FontAwesomeIcon
|
||||
icon={faClose}
|
||||
className="w-[2rem] h-[2rem] hover:text-primary cursor-pointer"
|
||||
className="hover:text-primary h-[2rem] w-[2rem] cursor-pointer"
|
||||
onClick={onRequestClose}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4 py-4">
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="flex items-center gap-4">
|
||||
<Image
|
||||
alt={project.title}
|
||||
src={project.coverImage}
|
||||
@@ -52,7 +52,7 @@ const PaymentModal: React.FC<ModalProps> = ({
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<h2 className="font-sans font-bold">{project.title}</h2>
|
||||
<h3 className="font-sans text-textgray">Pledge your support</h3>
|
||||
<h3 className="text-textgray font-sans">Pledge your support</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import ProgressBar from 'react-bootstrap/ProgressBar';
|
||||
|
||||
type ProgressProps = {
|
||||
text: number;
|
||||
};
|
||||
|
||||
const AnimatedExample = (props: ProgressProps) => {
|
||||
const { text } = props;
|
||||
return <ProgressBar animated variant="success" now={text} label={`${text}%`} />;
|
||||
percent: number
|
||||
}
|
||||
|
||||
export default AnimatedExample;
|
||||
const Progress = ({ percent }: ProgressProps) => {
|
||||
return (
|
||||
<div className="w-full flex flex-col items-center">
|
||||
<div className="w-full bg-gray-200 rounded-full h-4">
|
||||
<div
|
||||
className="bg-green-500 h-4 rounded-full text-xs"
|
||||
style={{ width: `${percent}%` }}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<span className="text-sm font-semibold">{percent}%</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Progress
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import escapeHTML from 'escape-html'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import { ProjectItem } from '../utils/types'
|
||||
import ShareButtons from './ShareButtons'
|
||||
|
||||
export type ProjectCardProps = {
|
||||
project: ProjectItem
|
||||
openPaymentModal: (project: ProjectItem) => void
|
||||
customImageStyles?: React.CSSProperties
|
||||
}
|
||||
|
||||
const ProjectCard: React.FC<ProjectCardProps> = ({
|
||||
project,
|
||||
openPaymentModal,
|
||||
customImageStyles,
|
||||
}) => {
|
||||
const { slug, title, summary, coverImage, git, twitter, personalTwitter, personalWebsite, nym, goal, isFunded } =
|
||||
project
|
||||
const [isHorizontal, setIsHorizontal] = useState<boolean | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const img = document.createElement('img')
|
||||
img.src = project.coverImage
|
||||
|
||||
// check if image is horizontal - added additional 10% to height to ensure only true
|
||||
// horizontals get flagged.
|
||||
img.onload = () => {
|
||||
const { naturalWidth, naturalHeight } = img
|
||||
const isHorizontal = naturalWidth >= naturalHeight * 1.1
|
||||
setIsHorizontal(isHorizontal)
|
||||
}
|
||||
}, [project.coverImage])
|
||||
|
||||
let cardStyle
|
||||
// if (tags.includes('Nostr')) {
|
||||
// cardStyle =
|
||||
// 'h-full space-y-4 rounded-xl border-b-4 border-purple-600 bg-stone-100 dark:border-purple-600 dark:bg-stone-900'
|
||||
// } else if (tags.includes('Lightning')) {
|
||||
// cardStyle =
|
||||
// 'h-full space-y-4 rounded-xl border-b-4 border-yellow-300 bg-stone-100 dark:border-yellow-300 dark:bg-stone-900'
|
||||
// } else if (tags.includes('Bitcoin')) {
|
||||
// cardStyle =
|
||||
// 'h-full space-y-4 rounded-xl border-b-4 border-orange-400 bg-stone-100 dark:border-orange-400 dark:bg-stone-900'
|
||||
// } else {
|
||||
cardStyle =
|
||||
'h-full space-y-4 rounded-xl border-b-4 border-orange-500 bg-stone-100 dark:bg-stone-900'
|
||||
// }
|
||||
|
||||
return (
|
||||
<figure className=" bg-white space-y-4 border border-lightgray rounded-xl h-full">
|
||||
<div className="relative h-64 w-full">
|
||||
<Link href={`/projects/${escapeHTML(slug)}`} passHref>
|
||||
<div className='relative h-64 w-full'>
|
||||
<Image
|
||||
alt={title}
|
||||
src={coverImage}
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
objectPosition="50% 50%"
|
||||
className="rounded-t-xl border border-lightgray cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<figcaption className="p-4 space-y-4">
|
||||
<h2>{title}</h2>
|
||||
<p>
|
||||
by{' '}
|
||||
<Link href={escapeHTML(personalWebsite)} passHref legacyBehavior>
|
||||
<a className="projectlist">{nym}</a>
|
||||
</Link>
|
||||
</p>
|
||||
<p className="prose line-clamp-3">{summary}</p>
|
||||
<div className="flex justify-end"></div>
|
||||
|
||||
<ShareButtons project={project} />
|
||||
<div className="flex space-x-4 items-center justify-center pt-4">
|
||||
{(isFunded)? `` : <button
|
||||
className="bg-black basis-1/2"
|
||||
onClick={() => openPaymentModal(project)}
|
||||
>
|
||||
Donate
|
||||
</button> }
|
||||
<div className="flex items-center justify-center basis-1/2">
|
||||
<Link href={`/projects/${escapeHTML(slug)}`} passHref legacyBehavior>
|
||||
<a className="projectlist">View Details</a>
|
||||
</Link>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="ml-1 w-4 h-4 text-textgray cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<figure className={cardStyle}>
|
||||
<Link href={`/projects/${project.slug}`} passHref>
|
||||
<div className="flex h-36 w-full sm:h-52">
|
||||
<Image
|
||||
alt={project.title}
|
||||
src={project.coverImage}
|
||||
width={1200}
|
||||
height={1200}
|
||||
style={{
|
||||
objectFit: 'cover',
|
||||
...customImageStyles,
|
||||
}}
|
||||
priority={true}
|
||||
className="cursor-pointer rounded-t-xl bg-white"
|
||||
/>
|
||||
</div>
|
||||
</figcaption>
|
||||
<figcaption className="p-2">
|
||||
<h2 className="font-bold">{project.title}</h2>
|
||||
<div className="mb-4 text-sm">by {project.nym}</div>
|
||||
<div className="mb-2 line-clamp-3">{project.summary}</div>
|
||||
</figcaption>
|
||||
</Link>
|
||||
</figure>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,37 +9,31 @@ type ProjectListProps = {
|
||||
header?: string
|
||||
exclude?: string
|
||||
projects: ProjectItem[]
|
||||
openPaymentModal: (project: ProjectItem) => void
|
||||
}
|
||||
|
||||
const ProjectList: React.FC<ProjectListProps> = ({
|
||||
header = 'Explore Projects',
|
||||
exclude,
|
||||
projects,
|
||||
openPaymentModal,
|
||||
}) => {
|
||||
const [sortedProjects, setSortedProjects] = useState<ProjectItem[]>()
|
||||
|
||||
useEffect(() => {
|
||||
setSortedProjects(projects.filter(p => p.slug !== exclude).sort(() => 0.5 - Math.random()))
|
||||
setSortedProjects(
|
||||
projects.filter((p) => p.slug !== exclude).sort(() => 0.5 - Math.random())
|
||||
)
|
||||
}, [projects])
|
||||
|
||||
return (
|
||||
<section className="p-4 md:p-8 flex flex-col items-center">
|
||||
<div className="flex justify-between items-center pb-8 w-full">
|
||||
<h1>{header}</h1>
|
||||
<div className="flex items-center">
|
||||
<Link href="/projects" className="projectlist">View All</Link>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="ml-1 w-4 h-4 text-textgray cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="grid md:grid-cols-3 gap-4 max-w-5xl">
|
||||
<section className="bg-light items-left flex flex-col">
|
||||
<ul className="grid max-w-5xl grid-cols-2 gap-4 md:grid-cols-4">
|
||||
{sortedProjects &&
|
||||
sortedProjects.slice(0, 3).map((p, i) => (
|
||||
sortedProjects.slice(0, 4).map((p, i) => (
|
||||
<li key={i} className="">
|
||||
<ProjectCard project={p} openPaymentModal={openPaymentModal} />
|
||||
<ProjectCard
|
||||
project={p}
|
||||
// tags={p.tags}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
13
components/SectionContainer.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function SectionContainer({ children }: Props) {
|
||||
return (
|
||||
<section className="mx-2 max-w-6xl px-4 sm:px-6 lg:mx-auto xl:mx-auto xl:max-w-5xl xl:px-0">
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
41
components/ThemeSwitch.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
const ThemeSwitch = () => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
const { theme, setTheme, resolvedTheme } = useTheme()
|
||||
|
||||
// When mounted on client, now we can show the UI
|
||||
useEffect(() => setMounted(true), [])
|
||||
|
||||
return (
|
||||
<button
|
||||
aria-label="Toggle Dark Mode"
|
||||
className="ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4"
|
||||
onClick={() =>
|
||||
setTheme(
|
||||
theme === 'dark' || resolvedTheme === 'dark' ? 'light' : 'dark'
|
||||
)
|
||||
}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
{mounted && (theme === 'dark' || resolvedTheme === 'dark') ? (
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
) : (
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
|
||||
)}
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default ThemeSwitch
|
||||
29
components/Typing.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import Typed from 'typed.js'
|
||||
|
||||
export default function Typing() {
|
||||
const el = React.useRef(null)
|
||||
|
||||
React.useEffect(() => {
|
||||
const typed = new Typed(el.current, {
|
||||
strings: [
|
||||
'Monero Fund',
|
||||
'Open Source Software',
|
||||
'The Monero Project',
|
||||
],
|
||||
typeSpeed: 50,
|
||||
loop: true,
|
||||
cursorChar: '_',
|
||||
})
|
||||
|
||||
return () => {
|
||||
typed.destroy()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<span ref={el} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
components/social-icons/facebook.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Facebook icon</title><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
1
components/social-icons/github.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub icon</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
|
After Width: | Height: | Size: 827 B |
1
components/social-icons/globe-solid.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
54
components/social-icons/index.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import Mail from './mail.svg'
|
||||
import Github from './github.svg'
|
||||
import Facebook from './facebook.svg'
|
||||
import Youtube from './youtube.svg'
|
||||
import Linkedin from './linkedin.svg'
|
||||
import Twitter from './twitter.svg'
|
||||
import Nostr from './nostr.svg'
|
||||
import Web from './globe-solid.svg'
|
||||
|
||||
// Icons taken from: https://simpleicons.org/ and https://fontawesome.com (globe)
|
||||
|
||||
const components = {
|
||||
mail: Mail,
|
||||
github: Github,
|
||||
facebook: Facebook,
|
||||
youtube: Youtube,
|
||||
linkedin: Linkedin,
|
||||
twitter: Twitter,
|
||||
nostr: Nostr,
|
||||
website: Web,
|
||||
}
|
||||
|
||||
type Props = {
|
||||
kind: keyof typeof components
|
||||
href: string
|
||||
size?: number
|
||||
}
|
||||
|
||||
const SocialIcon = ({ kind, href, size = 5 }: Props) => {
|
||||
if (
|
||||
!href ||
|
||||
(kind === 'mail' &&
|
||||
!/^mailto:\w+([.-]?\w+)@\w+([.-]?\w+)(.\w{2,3})+$/.test(href))
|
||||
)
|
||||
return <></>
|
||||
|
||||
const SocialSvg = components[kind]
|
||||
|
||||
return (
|
||||
<a
|
||||
className="text-sm text-gray-500 transition hover:text-gray-600"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={href}
|
||||
>
|
||||
<span className="sr-only">{kind}</span>
|
||||
<SocialSvg
|
||||
className={`fill-current text-gray-700 hover:text-orange-500 dark:text-gray-200 dark:hover:text-orange-500 h-${size} w-${size}`}
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export default SocialIcon
|
||||
1
components/social-icons/linkedin.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn icon</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||
|
After Width: | Height: | Size: 615 B |
4
components/social-icons/mail.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 224 B |
1
components/social-icons/nostr.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 770 B |
1
components/social-icons/twitter.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Twitter icon</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>
|
||||
|
After Width: | Height: | Size: 607 B |
1
components/social-icons/youtube.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>YouTube icon</title><path d="M23.499 6.203a3.008 3.008 0 00-2.089-2.089c-1.87-.501-9.4-.501-9.4-.501s-7.509-.01-9.399.501a3.008 3.008 0 00-2.088 2.09A31.258 31.26 0 000 12.01a31.258 31.26 0 00.523 5.785 3.008 3.008 0 002.088 2.089c1.869.502 9.4.502 9.4.502s7.508 0 9.399-.502a3.008 3.008 0 002.089-2.09 31.258 31.26 0 00.5-5.784 31.258 31.26 0 00-.5-5.808zm-13.891 9.4V8.407l6.266 3.604z"/></svg>
|
||||
|
After Width: | Height: | Size: 474 B |
7
data/headerNavLinks.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
const headerNavLinks = [
|
||||
{title: 'Apply', href: '/apply', isButton: false},
|
||||
{title: 'FAQs', href: '/faq', isButton: false},
|
||||
{title: 'About', href: '/about', isButton: false}
|
||||
]
|
||||
|
||||
export default headerNavLinks
|
||||
@@ -2,6 +2,14 @@
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
output: 'standalone',
|
||||
webpack: (config, options) => {
|
||||
config.module.rules.push({
|
||||
test: /\.svg$/,
|
||||
use: ['@svgr/webpack'],
|
||||
})
|
||||
|
||||
return config
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
5504
package-lock.json
generated
@@ -10,7 +10,6 @@
|
||||
"watch": "npm-watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/source-code-pro": "^5.0.18",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
||||
@@ -18,9 +17,11 @@
|
||||
"@sendgrid/mail": "^8.1.3",
|
||||
"@stripe/react-stripe-js": "^2.7.1",
|
||||
"@stripe/stripe-js": "^3.4.1",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@types/escape-html": "^1.0.4",
|
||||
"base-64": "^1.0.0",
|
||||
"bootstrap": "^5.3.3",
|
||||
"classnames": "^2.5.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"escape-html": "^1.0.3",
|
||||
"exclude": "^1.0.0",
|
||||
@@ -29,8 +30,8 @@
|
||||
"micro": "^10.0.1",
|
||||
"micro-cors": "^0.1.1",
|
||||
"next": "^14.2.3",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-modal": "^3.16.1",
|
||||
@@ -41,11 +42,13 @@
|
||||
"sharp": "^0.33.4",
|
||||
"stripe": "^15.9.0",
|
||||
"swr": "^2.2.5",
|
||||
"typed.js": "^2.1.0",
|
||||
"watch": "^0.13.0",
|
||||
"wicg-inert": "^3.1.2",
|
||||
"xss": "^1.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/base-64": "^1.0.2",
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import '@fontsource/source-code-pro/400.css'
|
||||
import '@fontsource/source-code-pro/600.css'
|
||||
import '@fontsource/source-code-pro/800.css'
|
||||
import '../styles/globals.css'
|
||||
import '../node_modules/bootstrap/dist/css/bootstrap.css'
|
||||
import { useEffect } from "react";
|
||||
import type { AppProps } from 'next/app'
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
import '../styles/globals.css'
|
||||
import '../styles/tailwind.css'
|
||||
|
||||
import Layout from '../components/Layout'
|
||||
import Head from 'next/head'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
useEffect(() => {
|
||||
require("../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ThemeProvider attribute="class" defaultTheme='system'>
|
||||
<Head>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||
</Head>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
</Layout>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import markdownToHtml from '../utils/markdownToHtml'
|
||||
import { getSingleFile } from '../utils/md'
|
||||
import BigDumbMarkdown from '../components/BigDumbMarkdown'
|
||||
import xss from 'xss'
|
||||
|
||||
export default function About({ content }: { content: string }) {
|
||||
return <BigDumbMarkdown content={content} />
|
||||
return (
|
||||
<article
|
||||
className="prose max-w-3xl mx-auto pb-8 pt-8 dark:prose-dark xl:col-span-2"
|
||||
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
|
||||
334
pages/apply.tsx
@@ -4,7 +4,7 @@ import { useForm } from 'react-hook-form'
|
||||
import { fetchPostJSON } from '../utils/api-helpers'
|
||||
import Link from 'next/link'
|
||||
export default function Apply() {
|
||||
async function handleClick() { }
|
||||
async function handleClick() {}
|
||||
const [loading, setLoading] = useState(false)
|
||||
const router = useRouter()
|
||||
const {
|
||||
@@ -26,62 +26,94 @@ export default function Apply() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col items-center justify-center gap-4 py-8">
|
||||
<div className="mx-auto flex-1 flex flex-col items-center justify-center gap-4 py-8 prose dark:prose-dark">
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="apply flex flex-col gap-4 p-4 max-w-2xl"
|
||||
>
|
||||
{/* {errors.exampleRequired && <span>This field is required</span>} */}
|
||||
<div className="prose">
|
||||
<h1>Application for Monero Fund Project Listing or General Fund Grant</h1>
|
||||
<p>
|
||||
Thanks for your interest in the Monero Fund!
|
||||
</p>
|
||||
<div>
|
||||
<h1>
|
||||
Application for Monero Fund Project Listing or General Fund Grant
|
||||
</h1>
|
||||
<p>Thanks for your interest in the Monero Fund!</p>
|
||||
<p>
|
||||
We're incredibly grateful to contributors like you working to
|
||||
support Monero, Bitcoin and other free and open source projects.
|
||||
</p>
|
||||
<p>
|
||||
The MAGIC Monero Fund is offering a grant program and fundraising platform to support Monero research and development,
|
||||
especially relating to privacy, security, user experience, and efficiency.
|
||||
Proposals can be related to the Monero protocol directly, or they can be related to other
|
||||
areas of the Monero ecosystem. For research projects, please refer to special instructions
|
||||
The MAGIC Monero Fund is offering a grant program and fundraising
|
||||
platform to support Monero research and development, especially
|
||||
relating to privacy, security, user experience, and efficiency.
|
||||
Proposals can be related to the Monero protocol directly, or they
|
||||
can be related to other areas of the Monero ecosystem. For research
|
||||
projects, please refer to special instructions
|
||||
<Link href="/apply_research"> here</Link>.
|
||||
</p>
|
||||
<h2>Proposal Evaluation Criteria</h2>
|
||||
<div>
|
||||
Submitted proposals will be evaluated by the committee based on the following criteria:
|
||||
Submitted proposals will be evaluated by the committee based on the
|
||||
following criteria:
|
||||
<ul>
|
||||
<li><b>Impact:</b> The proposal should have a clear impact on the Monero Project.</li>
|
||||
<li><b>Originality:</b> The proposal should be original and not a rehash of existing work.</li>
|
||||
<li><b>Feasibility:</b> The proposal should be feasible to complete within the proposed time frame.</li>
|
||||
<li><b>Quality:</b> The proposal should be well-written and well-organized.</li>
|
||||
<li><b>Relevance:</b> The proposal should be relevant to the Monero Project.</li>
|
||||
<li>
|
||||
<b>Impact:</b> The proposal should have a clear impact on the
|
||||
Monero Project.
|
||||
</li>
|
||||
<li>
|
||||
<b>Originality:</b> The proposal should be original and not a
|
||||
rehash of existing work.
|
||||
</li>
|
||||
<li>
|
||||
<b>Feasibility:</b> The proposal should be feasible to complete
|
||||
within the proposed time frame.
|
||||
</li>
|
||||
<li>
|
||||
<b>Quality:</b> The proposal should be well-written and
|
||||
well-organized.
|
||||
</li>
|
||||
<li>
|
||||
<b>Relevance:</b> The proposal should be relevant to the Monero
|
||||
Project.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h2 id="Eligibility">Eligibility</h2>
|
||||
<p>
|
||||
All qualified researchers are eligible to apply, regardless of educational attainment or occupation.
|
||||
However, as a nonprofit organization registered under U.S. tax laws, MAGIC Grants is required to comply
|
||||
with certain laws when disbursing funds to grant recipients.
|
||||
Grant recipients must complete a Due Diligence checklist,
|
||||
which are the last two pages of <a href="https://magicgrants.org/funds/MAGIC%20Fund%20Grant%20Disbursement%20Process%20and%20Requirements.pdf">this document</a>.
|
||||
This includes the collection of your ID and tax information. We will conduct sanctions checks.
|
||||
All qualified researchers are eligible to apply, regardless of
|
||||
educational attainment or occupation. However, as a nonprofit
|
||||
organization registered under U.S. tax laws, MAGIC Grants is
|
||||
required to comply with certain laws when disbursing funds to grant
|
||||
recipients. Grant recipients must complete a Due Diligence
|
||||
checklist, which are the last two pages of{' '}
|
||||
<a href="https://magicgrants.org/funds/MAGIC%20Fund%20Grant%20Disbursement%20Process%20and%20Requirements.pdf">
|
||||
this document
|
||||
</a>
|
||||
. This includes the collection of your ID and tax information. We
|
||||
will conduct sanctions checks.
|
||||
</p>
|
||||
<h2>How to Submit a Proposal</h2>
|
||||
<p>
|
||||
To submit a proposal, please complete the form below or create an issue on <a href="https://github.com/MAGICGrants/Monero-Fund/issues/new?assignees=&labels=&template=grant-application.md&title=[Grant+Title]">Github</a>.
|
||||
Applicants are free to use their legal name or a pseudonym at this step,
|
||||
although note the <a href="#Eligibility"><b>Eligibility</b></a> section.
|
||||
You can choose to apply for a direct grant from the MAGIC Monero Fund's General Fund and/or
|
||||
request that your project be listed on MoneroFund.org to raise funds from Monero
|
||||
community members.
|
||||
To submit a proposal, please complete the form below or create an
|
||||
issue on{' '}
|
||||
<a href="https://github.com/MAGICGrants/Monero-Fund/issues/new?assignees=&labels=&template=grant-application.md&title=[Grant+Title]">
|
||||
Github
|
||||
</a>
|
||||
. Applicants are free to use their legal name or a pseudonym at this
|
||||
step, although note the{' '}
|
||||
<a href="#Eligibility">
|
||||
<b>Eligibility</b>
|
||||
</a>{' '}
|
||||
section. You can choose to apply for a direct grant from the MAGIC
|
||||
Monero Fund's General Fund and/or request that your project be
|
||||
listed on MoneroFund.org to raise funds from Monero community
|
||||
members.
|
||||
</p>
|
||||
<p>
|
||||
Please note this form is not considered confidential and is effectively equivalent to a public GitHub issue.
|
||||
In order to reach out privately, please send an email to MoneroFund@magicgrants.org.
|
||||
Please note this form is not considered confidential and is
|
||||
effectively equivalent to a public GitHub issue. In order to reach
|
||||
out privately, please send an email to MoneroFund@magicgrants.org.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<label className="checkbox">
|
||||
<input type="checkbox" {...register('general_fund')} />
|
||||
Apply to receive a grant from the Magic Monero Fund.
|
||||
@@ -92,118 +124,192 @@ export default function Apply() {
|
||||
Apply for project to be listed on the Monero Fund Donation Page.
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Project Name *
|
||||
<small>
|
||||
The name of the project to be listed for fundraising
|
||||
</small>
|
||||
<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 })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
Your Name or Pseudonym *
|
||||
<input type="text" {...register('your_name', { required: true })} />
|
||||
</label>
|
||||
<label>
|
||||
Email *
|
||||
<input type="text" {...register('email', { required: true })} />
|
||||
</label>
|
||||
<label>
|
||||
Project Github (if applicable)
|
||||
<input type="text" {...register('github')} />
|
||||
</label>
|
||||
<label>
|
||||
Personal Github (if applicable)
|
||||
<input type="text" {...register('personal_github')} />
|
||||
</label>
|
||||
<label>
|
||||
Other Contact Details (if applicable)
|
||||
<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.
|
||||
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>
|
||||
<small>
|
||||
These could include github username, twitter username, LinkedIn,
|
||||
reddit handle, other social media handles, emails, phone
|
||||
numbers, usernames, etc.
|
||||
</small>
|
||||
<textarea {...register('other_contact')} />
|
||||
</label>
|
||||
<label>
|
||||
Short Project Description *
|
||||
<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 Monero Fund
|
||||
website. 2-3 sentences.
|
||||
</small>
|
||||
<textarea {...register('short_description', { required: true })} />
|
||||
</label>
|
||||
<label>
|
||||
Long Project Description
|
||||
<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 Monero Fund
|
||||
website. It can be longer and go into detail about your project.
|
||||
</small>
|
||||
<textarea {...register('long_description')} />
|
||||
</label>
|
||||
{/* <label>
|
||||
Project Images: (attachment)
|
||||
<input type="text" {...register('your_name', { required: true })} />
|
||||
</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="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>
|
||||
<label>
|
||||
If someone else, please list the project's Lead Contributor or
|
||||
Maintainer <input type="text" {...register('other_lead')} />
|
||||
</label>
|
||||
<label>
|
||||
Potential Impact *
|
||||
<small>
|
||||
Why is this project important to the Monero community?
|
||||
</small>
|
||||
<textarea {...register('potential_impact', { required: true })} />
|
||||
</label>
|
||||
<label>
|
||||
Project Timelines and Potential Milestones *
|
||||
<textarea {...register('timelines', { required: true })} />
|
||||
</label>
|
||||
<label>
|
||||
If you're applying for a grant from the general fund, please
|
||||
submit a proposed budget for the requested amount and how it will be used.
|
||||
<input type="text" {...register('proposed_budget')} />
|
||||
</label>
|
||||
<label>
|
||||
Applicant Bios (Optional)
|
||||
<small>
|
||||
List revevant accomplishments.
|
||||
</small>
|
||||
<textarea {...register('bios')} />
|
||||
</label>
|
||||
<div className="prose">
|
||||
<small>
|
||||
The MAGIC Monero 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, in
|
||||
accordance with US law.
|
||||
</small>
|
||||
|
||||
<div className="w-full flex flex-col">
|
||||
<label htmlFor="other_lead">
|
||||
If someone else, please list the project'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>
|
||||
|
||||
<button type="submit" disabled={loading}>
|
||||
<div className="w-full flex flex-col">
|
||||
<label htmlFor="potential_impact">Potential Impact *</label>
|
||||
<small>Why is this project important to the Monero 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'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 Monero 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, in accordance with US law.
|
||||
</small>
|
||||
|
||||
<button
|
||||
className="mb-2 mr-2 w-full rounded bg-orange-500 px-4 text-xl font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-black dark:text-black dark:hover:text-white md:max-w-[98%] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
disabled={loading}
|
||||
>
|
||||
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:monerofund@magicgrants.org">monerofund@magicgrants.org</a> if you have any questions.
|
||||
After submitting your application, please allow our team up to three
|
||||
weeks to review your application. Email us at{' '}
|
||||
<a href="mailto:monerofund@magicgrants.org">
|
||||
monerofund@magicgrants.org
|
||||
</a>{' '}
|
||||
if you have any questions.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import markdownToHtml from '../utils/markdownToHtml'
|
||||
import { getSingleFile } from '../utils/md'
|
||||
import BigDumbMarkdown from '../components/BigDumbMarkdown'
|
||||
import xss from 'xss'
|
||||
|
||||
export default function Faq({ content }: { content: string }) {
|
||||
return <BigDumbMarkdown content={content} />
|
||||
return (
|
||||
<article
|
||||
className="prose max-w-3xl mx-auto pb-8 pt-8 dark:prose-dark xl:col-span-2"
|
||||
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
|
||||
@@ -10,6 +10,8 @@ import magiclogo from '/public/img/crystalball.jpg'
|
||||
import { getAllPosts } from '../utils/md'
|
||||
import { ProjectItem } from '../utils/types'
|
||||
import { useRouter } from 'next/router'
|
||||
import Typing from '../components/Typing'
|
||||
import CustomLink from '../components/CustomLink'
|
||||
|
||||
// These shouldn't be swept up in the regular list so we hardcode them
|
||||
const generalFund: ProjectItem = {
|
||||
@@ -18,8 +20,7 @@ const generalFund: ProjectItem = {
|
||||
website: 'https://monerofund.org',
|
||||
personalWebsite: 'https://monerofund.org',
|
||||
title: 'MAGIC Monero General Fund',
|
||||
summary:
|
||||
'Support contributors to Monero',
|
||||
summary: 'Support contributors to Monero',
|
||||
coverImage: '/img/crystalball.jpg',
|
||||
git: 'magicgrants',
|
||||
twitter: 'magicgrants',
|
||||
@@ -48,7 +49,7 @@ const Home: NextPage<{ projects: any }> = ({ projects }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (router.isReady) {
|
||||
console.log(router.query);
|
||||
console.log(router.query)
|
||||
}
|
||||
}, [router.isReady])
|
||||
|
||||
@@ -59,31 +60,62 @@ const Home: NextPage<{ projects: any }> = ({ projects }) => {
|
||||
<meta name="description" content="TKTK" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main>
|
||||
<section id="root" className="flex flex-col sm:flex-row">
|
||||
<div className="flex-auto p-4 md:p-8 space-y-8 sm:order-first">
|
||||
<h1>
|
||||
Support the Monero Fund and open source software and research for the Monero Project.
|
||||
</h1>
|
||||
<p className="text-textgray">
|
||||
Donations are tax-deductible in the USA as MAGIC is a 501(c)(3) charity
|
||||
</p>
|
||||
<button role={'button'} onClick={openGeneralFundModal}>
|
||||
Donate to Monero Committee General Fund
|
||||
</button>
|
||||
<p>
|
||||
Want to receive funding for your work? {' '}
|
||||
<Link href="/apply" legacyBehavior >
|
||||
<a className="custom-link">Apply for a Monero development or research grant!</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="pt-4 md:pb-8">
|
||||
<h1 className="py-4 text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
|
||||
Support <Typing />
|
||||
</h1>
|
||||
<p className="text-xl leading-7 text-gray-500 dark:text-gray-400">
|
||||
Help us to provide sustainable funding for free and open-source
|
||||
contributors working on freedom tech and projects that help Monero
|
||||
flourish.
|
||||
</p>
|
||||
<div className="flex flex-wrap py-4">
|
||||
<div className="w-full md:w-1/2">
|
||||
<button
|
||||
onClick={openGeneralFundModal}
|
||||
className="mb-2 mr-2 w-full rounded bg-orange-500 px-4 text-xl font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-black dark:text-black dark:hover:text-white md:max-w-[98%]"
|
||||
>
|
||||
Donate to Monero Comittee General Fund
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="order-first flex-auto my-auto">
|
||||
<Image className="img-fluid" src={magiclogo} alt="magiclogo" />
|
||||
<p className="text-lg leading-7 text-gray-500 dark:text-gray-400">
|
||||
Want to receive funding for your work?
|
||||
<CustomLink href="/apply" className="text-orange-500">
|
||||
{' '}
|
||||
Apply for a Monero development or research grant!
|
||||
</CustomLink>
|
||||
</p>
|
||||
|
||||
<p className="text-md leading-7 text-gray-500 dark:text-gray-400">
|
||||
We are a 501(c)(3) public charity. All donations are tax deductible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="xl:pt-18 space-y-2 pt-8 md:space-y-5">
|
||||
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
|
||||
Explore Projects
|
||||
</h1>
|
||||
<p className="pt-2 text-lg leading-7 text-gray-500 dark:text-gray-400">
|
||||
Browse through a showcase of projects supported by us.
|
||||
</p>
|
||||
<ProjectList projects={projects} />
|
||||
<div className="flex justify-end pt-4 text-base font-medium leading-6">
|
||||
<Link
|
||||
href="/projects"
|
||||
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
|
||||
aria-label="View All Projects"
|
||||
>
|
||||
View Projects →
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
<ProjectList projects={projects} openPaymentModal={openPaymentModal} />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PaymentModal
|
||||
isOpen={modalOpen}
|
||||
onRequestClose={closeModal}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextPage } from 'next/types'
|
||||
import ErrorPage from 'next/error'
|
||||
import sanitize from 'sanitize-filename'
|
||||
import Image from 'next/image'
|
||||
import xss from 'xss'
|
||||
|
||||
import { ProjectItem, Stats } from '../../utils/types'
|
||||
import { getPostBySlug, getAllPosts } from '../../utils/md'
|
||||
import markdownToHtml from '../../utils/markdownToHtml'
|
||||
import markdownStyles from '../../components/markdown-styles.module.css'
|
||||
import Image from 'next/image'
|
||||
import ProjectList from '../../components/ProjectList'
|
||||
import BackToProjects from '../../components/BackToProjects'
|
||||
import { ProjectItem, Stats } from '../../utils/types'
|
||||
import { NextPage } from 'next/types'
|
||||
import { useEffect, useState } from 'react'
|
||||
import {
|
||||
fetchPostJSON,
|
||||
fetchGetJSONAuthedBTCPay,
|
||||
fetchGetJSONAuthedStripe,
|
||||
} from '../../utils/api-helpers'
|
||||
import PaymentModal from '../../components/PaymentModal'
|
||||
import Link from 'next/link'
|
||||
import ShareButtons from '../../components/ShareButtons'
|
||||
import PageHeading from '../../components/PageHeading'
|
||||
import SocialIcon from '../../components/social-icons'
|
||||
import Progress from '../../components/Progress'
|
||||
import { fetchPostJSON, fetchGetJSONAuthedBTCPay, fetchGetJSONAuthedStripe } from '../../utils/api-helpers'
|
||||
import xss from 'xss'
|
||||
|
||||
type SingleProjectPageProps = {
|
||||
project: ProjectItem
|
||||
@@ -23,9 +24,11 @@ type SingleProjectPageProps = {
|
||||
stats: Stats
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats }) => {
|
||||
const Project: NextPage<SingleProjectPageProps> = ({
|
||||
project,
|
||||
projects,
|
||||
stats,
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
@@ -58,10 +61,9 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
|
||||
isFunded,
|
||||
} = project
|
||||
|
||||
|
||||
function formatBtc(bitcoin: number) {
|
||||
if (bitcoin > 0.1) {
|
||||
return `₿ ${bitcoin.toFixed(3) || 0.00}`;
|
||||
return `₿ ${bitcoin.toFixed(3) || 0.0}`
|
||||
} else {
|
||||
return `${Math.floor(bitcoin * 100000000).toLocaleString()} sats`
|
||||
}
|
||||
@@ -69,23 +71,92 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
|
||||
|
||||
function formatUsd(dollars: number): string {
|
||||
if (dollars == 0) {
|
||||
return ""
|
||||
} else if
|
||||
(dollars / 1000 > 1) {
|
||||
return `+ $${Math.round(dollars / 1000)}k`
|
||||
return '$0'
|
||||
} else if (dollars / 1000 > 1) {
|
||||
return `$${Math.round(dollars / 1000)}k+`
|
||||
} else {
|
||||
return `+ $${dollars.toFixed(0)}`
|
||||
return `$${dollars.toFixed(0)}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!router.isFallback && !slug) {
|
||||
return <ErrorPage statusCode={404} />
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className={"h-[15rem] w-full relative bg-gradient-to-b from-white to-gray-200"}>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<PageHeading project={project}>
|
||||
<div className="flex flex-col items-center space-x-2 pt-8 xl:block">
|
||||
<Image
|
||||
src={coverImage}
|
||||
alt="avatar"
|
||||
width={300}
|
||||
height={300}
|
||||
className="h-72 w-72 mx-auto object-contain xl:hidden"
|
||||
/>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h1 className="mb-4 font-bold">Raised</h1>
|
||||
|
||||
<Progress
|
||||
percent={Math.floor(
|
||||
((stats[slug].xmr.totaldonationsinfiat +
|
||||
stats[slug].btc.totaldonationsinfiat +
|
||||
stats[slug].usd.totaldonationsinfiat) /
|
||||
goal) *
|
||||
100
|
||||
)}
|
||||
/>
|
||||
|
||||
<ul className="font-semibold space-y-1">
|
||||
<li className="flex items-center space-x-1">
|
||||
<span className="text-green-500 text-xl">{`${formatUsd(stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)}`}</span>{' '}
|
||||
<span className="font-normal text-sm text-gray">
|
||||
in{' '}
|
||||
{stats[slug].xmr.numdonations +
|
||||
stats[slug].btc.numdonations +
|
||||
stats[slug].usd.numdonations}{' '}
|
||||
donations total
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
{stats[slug].xmr.totaldonations} XMR{' '}
|
||||
<span className="font-normal text-sm text-gray">
|
||||
in {stats[slug].xmr.numdonations} {''}
|
||||
donations
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
{stats[slug].btc.totaldonations} BTC{' '}
|
||||
<span className="font-normal text-sm text-gray">
|
||||
in {stats[slug].btc.numdonations} {''}
|
||||
donations
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
{`${formatUsd(stats[slug].usd.totaldonations)}`} Fiat{' '}
|
||||
<span className="font-normal text-sm text-gray">
|
||||
in {stats[slug].usd.numdonations} {''}
|
||||
donations
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article
|
||||
className="prose max-w-none pb-8 pt-8 dark:prose-dark xl:col-span-2"
|
||||
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
|
||||
/>
|
||||
</PageHeading>
|
||||
</div>
|
||||
|
||||
{/* <div className="flex flex-col items-center">
|
||||
<div
|
||||
className={
|
||||
'h-[15rem] w-full relative bg-gradient-to-b from-white to-gray-200'
|
||||
}
|
||||
>
|
||||
<Image
|
||||
alt={title}
|
||||
src={coverImage}
|
||||
@@ -95,12 +166,15 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full p-4 py-8 md:px-8">
|
||||
</div>
|
||||
<div className="flex w-full p-4 py-8 md:px-8"></div>
|
||||
<article className="px-4 md:px-8 pb-8 lg:flex lg:flex-row-reverse lg:items-start">
|
||||
<div className={markdownStyles['markdown']}>
|
||||
|
||||
</div>
|
||||
|
||||
<aside className="p-4 bg-light rounded-xl flex lg:flex-col lg:items-start gap-4 min-w-[20rem] justify-between items-center mb-8">
|
||||
{(isFunded)? `` : <button onClick={openPaymentModal}>Donate</button> }
|
||||
{stats &&
|
||||
{isFunded ? `` : <button onClick={openPaymentModal}>Donate</button>}
|
||||
{stats && (
|
||||
<div>
|
||||
<h5>Raised</h5>
|
||||
<h4>{`${formatUsd(stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)}`}</h4>
|
||||
@@ -108,40 +182,46 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
|
||||
<h6>{stats[slug].btc.totaldonations} BTC</h6>
|
||||
<h6>{`${formatUsd(stats[slug].usd.totaldonations)}`} Fiat</h6>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
|
||||
{stats && <div>
|
||||
<h5>Donations</h5>
|
||||
<h4>{stats[slug].xmr.numdonations + stats[slug].btc.numdonations + stats[slug].usd.numdonations}</h4>
|
||||
<h6>{stats[slug].xmr.numdonations} in XMR</h6>
|
||||
<h6>{stats[slug].btc.numdonations} in BTC</h6>
|
||||
<h6>{stats[slug].usd.numdonations} in Fiat</h6>
|
||||
{stats && (
|
||||
<div>
|
||||
<h5>Donations</h5>
|
||||
<h4>
|
||||
{stats[slug].xmr.numdonations +
|
||||
stats[slug].btc.numdonations +
|
||||
stats[slug].usd.numdonations}
|
||||
</h4>
|
||||
<h6>{stats[slug].xmr.numdonations} in XMR</h6>
|
||||
<h6>{stats[slug].btc.numdonations} in BTC</h6>
|
||||
<h6>{stats[slug].usd.numdonations} in Fiat</h6>
|
||||
|
||||
<Progress text={Math.floor((stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)/goal * 100) } ></Progress>
|
||||
</div>
|
||||
<Progress
|
||||
text={Math.floor(
|
||||
((stats[slug].xmr.totaldonationsinfiat +
|
||||
stats[slug].btc.totaldonationsinfiat +
|
||||
stats[slug].usd.totaldonationsinfiat) /
|
||||
goal) *
|
||||
100
|
||||
)}
|
||||
></Progress>
|
||||
</div>
|
||||
}
|
||||
</aside>
|
||||
</article>
|
||||
|
||||
<div className={markdownStyles['markdown']}>
|
||||
<Link href={website} legacyBehavior>
|
||||
<a className='!no-underline'>
|
||||
<h1>{title}</h1>
|
||||
</a>
|
||||
</Link>
|
||||
<p>{summary}</p>
|
||||
<aside className="bg-light mb-8 flex min-w-[20rem] items-center justify-between gap-4 rounded-xl p-4 lg:flex-col lg:items-start">
|
||||
{!isFunded && (
|
||||
<button
|
||||
onClick={openPaymentModal}
|
||||
className="block rounded border border-stone-800 bg-stone-800 px-4 py-2 font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-stone-800 dark:bg-white dark:text-black dark:hover:bg-orange-500"
|
||||
>
|
||||
Donate
|
||||
</button>
|
||||
)}
|
||||
</aside>
|
||||
</div> */}
|
||||
|
||||
<p>
|
||||
by{' '}
|
||||
<Link href={personalWebsite} passHref legacyBehavior>
|
||||
<a>{nym}</a>
|
||||
</Link >
|
||||
</p >
|
||||
<ShareButtons project={project} />
|
||||
<hr />
|
||||
{content && <div dangerouslySetInnerHTML={{ __html: xss(content) }} />}
|
||||
</div >
|
||||
</article >
|
||||
</div >
|
||||
<PaymentModal
|
||||
isOpen={modalOpen}
|
||||
onRequestClose={closeModal}
|
||||
@@ -160,34 +240,35 @@ export async function getServerSideProps({ params }: { params: any }) {
|
||||
|
||||
const content = await markdownToHtml(post.content || '')
|
||||
|
||||
let stats : any = {};
|
||||
let stats: any = {}
|
||||
|
||||
for(let i=0;i<projects.length;i++){
|
||||
let xmr;
|
||||
let btc;
|
||||
let usd;
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
let xmr
|
||||
let btc
|
||||
let usd
|
||||
|
||||
if (projects[i].isFunded) {
|
||||
xmr = {
|
||||
numdonations: projects[i].numdonationsxmr,
|
||||
totaldonationsinfiat: projects[i].totaldonationsinfiatxmr,
|
||||
totaldonations: projects[i].totaldonationsxmr,
|
||||
}
|
||||
btc = {
|
||||
numdonations: projects[i].numdonationsbtc,
|
||||
totaldonationsinfiat: projects[i].totaldonationsinfiatbtc,
|
||||
totaldonations: projects[i].totaldonationsbtc,
|
||||
}
|
||||
usd = {numdonations: projects[i].fiatnumdonations,
|
||||
totaldonationsinfiat: projects[i].fiattotaldonationsinfiat,
|
||||
totaldonations: projects[i].fiattotaldonations,
|
||||
xmr = {
|
||||
numdonations: projects[i].numdonationsxmr,
|
||||
totaldonationsinfiat: projects[i].totaldonationsinfiatxmr,
|
||||
totaldonations: projects[i].totaldonationsxmr,
|
||||
}
|
||||
} else {
|
||||
const crypto = await fetchGetJSONAuthedBTCPay(projects[i].slug)
|
||||
xmr = await crypto.xmr
|
||||
btc = await crypto.btc
|
||||
usd = await fetchGetJSONAuthedStripe(projects[i].slug)
|
||||
}
|
||||
btc = {
|
||||
numdonations: projects[i].numdonationsbtc,
|
||||
totaldonationsinfiat: projects[i].totaldonationsinfiatbtc,
|
||||
totaldonations: projects[i].totaldonationsbtc,
|
||||
}
|
||||
usd = {
|
||||
numdonations: projects[i].fiatnumdonations,
|
||||
totaldonationsinfiat: projects[i].fiattotaldonationsinfiat,
|
||||
totaldonations: projects[i].fiattotaldonations,
|
||||
}
|
||||
} else {
|
||||
const crypto = await fetchGetJSONAuthedBTCPay(projects[i].slug)
|
||||
xmr = await crypto.xmr
|
||||
btc = await crypto.btc
|
||||
usd = await fetchGetJSONAuthedStripe(projects[i].slug)
|
||||
}
|
||||
|
||||
stats[projects[i].slug] = { xmr, btc, usd }
|
||||
}
|
||||
@@ -203,5 +284,3 @@ export async function getServerSideProps({ params }: { params: any }) {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ const AllProjects: NextPage<{ projects: ProjectItem[] }> = ({ projects }) => {
|
||||
{sortedProjects &&
|
||||
sortedProjects.map((p, i) => (
|
||||
<li key={i} className="">
|
||||
<ProjectCard project={p} openPaymentModal={openPaymentModal} />
|
||||
<ProjectCard project={p} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -1,91 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
code {
|
||||
@apply break-all whitespace-normal select-all;
|
||||
}
|
||||
/* When in a code block, let XMR addresses wrap, but a click or tap will select whole address */
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
@apply font-mono font-semibold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-3xl md:text-4xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply text-2xl font-bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@apply text-textgray font-mono uppercase;
|
||||
}
|
||||
|
||||
button,
|
||||
button[type='submit'] {
|
||||
@apply py-2 px-4 bg-primary text-white rounded font-mono hover:shadow-glow;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
@apply bg-white text-black border border-primary;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-textgray underline underline-offset-2 hover:no-underline;
|
||||
}
|
||||
|
||||
.projectlist {
|
||||
color: rgb(242,95,19);
|
||||
@apply hover:no-underline no-underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-textgray;
|
||||
}
|
||||
|
||||
p a {
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
button.selected {
|
||||
@apply bg-primary;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='number'],
|
||||
input[type='email'],
|
||||
input[type='textarea'],
|
||||
textarea {
|
||||
@apply border border-lightgray rounded p-2 focus:border-black;
|
||||
}
|
||||
|
||||
input[type='textarea'] {
|
||||
@apply resize-y;
|
||||
}
|
||||
|
||||
input[type='radio'] {
|
||||
@apply mr-2;
|
||||
}
|
||||
|
||||
nav li a {
|
||||
@apply text-black no-underline ;
|
||||
}
|
||||
|
||||
form h3 {
|
||||
@apply font-sans font-normal;
|
||||
}
|
||||
|
||||
button.small {
|
||||
@apply p-1 text-sm bg-white text-black border border-black;
|
||||
}
|
||||
@@ -95,37 +7,28 @@ button.group {
|
||||
}
|
||||
|
||||
button.pay {
|
||||
@apply flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl text-black hover:text-primary rounded-xl flex justify-start gap-4;
|
||||
@apply rounded border border-orange-500 bg-transparent font-semibold text-orange-500 hover:border-transparent hover:bg-orange-500 hover:text-white flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl rounded-xl flex justify-start gap-4;
|
||||
}
|
||||
|
||||
button.pay:disabled {
|
||||
@apply bg-gray-100 border-gray-400 text-gray-400 hover:shadow-none;
|
||||
}
|
||||
|
||||
.credit a {
|
||||
@apply no-underline hover:underline;
|
||||
}
|
||||
|
||||
form.apply label {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
|
||||
form.apply label.checkbox {
|
||||
@apply flex flex-row items-center;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
@apply mr-2;
|
||||
}
|
||||
|
||||
.color-nav{
|
||||
background-color: rgb(242,95,19);
|
||||
.video-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-bottom: 56.25%;
|
||||
}
|
||||
|
||||
.custom-link{
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
.color-me-monero{
|
||||
color: rgb(242,95,19);
|
||||
.video-container iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
90
styles/tailwind.css
Normal file
@@ -0,0 +1,90 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.task-list-item::before {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.task-list-item {
|
||||
@apply list-none;
|
||||
}
|
||||
|
||||
.footnotes {
|
||||
@apply mt-12 border-t border-gray-200 pt-8 dark:border-gray-700;
|
||||
}
|
||||
|
||||
.data-footnote-backref {
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
.csl-entry {
|
||||
@apply my-5;
|
||||
}
|
||||
|
||||
/* https://stackoverflow.com/questions/61083813/how-to-avoid-internal-autofill-selected-style-to-be-applied */
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:focus {
|
||||
transition:
|
||||
background-color 600000s 0s,
|
||||
color 600000s 0s;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
h1 {
|
||||
@apply text-2xl;
|
||||
}
|
||||
h2 {
|
||||
@apply text-xl;
|
||||
}
|
||||
h3 {
|
||||
@apply text-lg;
|
||||
}
|
||||
}
|
||||
|
||||
button.small {
|
||||
@apply p-1 text-sm bg-white text-black border border-black;
|
||||
}
|
||||
|
||||
button.group {
|
||||
@apply p-2 text-sm bg-white text-black border border-black;
|
||||
}
|
||||
|
||||
button.pay {
|
||||
@apply flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl text-black rounded-xl flex justify-start gap-4;
|
||||
}
|
||||
|
||||
button.pay:disabled {
|
||||
@apply bg-gray-100 border-gray-400 text-gray-400 hover:shadow-none;
|
||||
}
|
||||
|
||||
button,
|
||||
button[type='submit'] {
|
||||
@apply py-2 px-4 text-white rounded;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
@apply bg-white text-black border;
|
||||
}
|
||||
|
||||
.prose > ul > li > p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.prose small {
|
||||
@apply leading-normal;
|
||||
}
|
||||
|
||||
form label {
|
||||
@apply text-sm font-semibold leading-normal;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
form input[type='text'],
|
||||
form textarea {
|
||||
@apply mt-2;
|
||||
}
|
||||
@@ -1,33 +1,175 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
// @ts-check
|
||||
const { fontFamily } = require('tailwindcss/defaultTheme')
|
||||
const colors = require('tailwindcss/colors')
|
||||
|
||||
// ../node_modules/pliny/dist/**/*.mjs is needed for monorepo setup
|
||||
/** @type {import("tailwindcss/types").Config } */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./pages/**/*.{js,ts,jsx,tsx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx}',
|
||||
'../node_modules/pliny/**/*.{js,ts,tsx}',
|
||||
'./node_modules/pliny/**/*.{js,ts,tsx}',
|
||||
'./app/**/*.{js,ts,jsx,tsx}',
|
||||
'./pages/**/*.{js,ts,tsx}',
|
||||
'./components/**/*.{js,ts,tsx}',
|
||||
'./layouts/**/*.{js,ts,tsx}',
|
||||
'./lib/**/*.{js,ts,tsx}',
|
||||
'./data/**/*.mdx',
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
boxShadow: {
|
||||
glow: '0px 0px 4px #FF6800',
|
||||
spacing: {
|
||||
'9/16': '56.25%',
|
||||
},
|
||||
backgroundImage: {
|
||||
'hero-bloom':
|
||||
'radial-gradient(circle, #e95d1c 4%, #e93e1c 61%, #e91f1c 100%)',
|
||||
},
|
||||
colors: {
|
||||
primary: '#FF6800',
|
||||
subtle: '#999999',
|
||||
lightgray: '#E5E5E5',
|
||||
textgray: '#4F4F4F',
|
||||
light: '#FAFAFA',
|
||||
lineHeight: {
|
||||
11: '2.75rem',
|
||||
12: '3rem',
|
||||
13: '3.25rem',
|
||||
14: '3.5rem',
|
||||
},
|
||||
fontFamily: {
|
||||
mono: ['Source Code Pro', ...defaultTheme.fontFamily.sans],
|
||||
sans: ['Inter', ...fontFamily.sans],
|
||||
},
|
||||
colors: {
|
||||
primary: colors.orange,
|
||||
gray: colors.neutral,
|
||||
},
|
||||
typography: (theme) => ({
|
||||
DEFAULT: {
|
||||
css: {
|
||||
color: theme('colors.gray.700'),
|
||||
a: {
|
||||
color: theme('colors.primary.500'),
|
||||
'&:hover': {
|
||||
color: `${theme('colors.primary.600')} !important`,
|
||||
},
|
||||
code: { color: theme('colors.primary.400') },
|
||||
},
|
||||
h1: {
|
||||
fontWeight: '700',
|
||||
letterSpacing: theme('letterSpacing.tight'),
|
||||
color: theme('colors.gray.900'),
|
||||
},
|
||||
h2: {
|
||||
fontWeight: '700',
|
||||
letterSpacing: theme('letterSpacing.tight'),
|
||||
color: theme('colors.gray.900'),
|
||||
},
|
||||
h3: {
|
||||
fontWeight: '600',
|
||||
color: theme('colors.gray.900'),
|
||||
},
|
||||
'h4,h5,h6': {
|
||||
color: theme('colors.gray.900'),
|
||||
},
|
||||
pre: {
|
||||
backgroundColor: theme('colors.gray.800'),
|
||||
},
|
||||
code: {
|
||||
color: theme('colors.pink.500'),
|
||||
backgroundColor: theme('colors.gray.100'),
|
||||
paddingLeft: '4px',
|
||||
paddingRight: '4px',
|
||||
paddingTop: '2px',
|
||||
paddingBottom: '2px',
|
||||
borderRadius: '0.25rem',
|
||||
},
|
||||
'code::before': {
|
||||
content: 'none',
|
||||
},
|
||||
'code::after': {
|
||||
content: 'none',
|
||||
},
|
||||
details: {
|
||||
backgroundColor: theme('colors.gray.100'),
|
||||
paddingLeft: '4px',
|
||||
paddingRight: '4px',
|
||||
paddingTop: '2px',
|
||||
paddingBottom: '2px',
|
||||
borderRadius: '0.25rem',
|
||||
},
|
||||
hr: { borderColor: theme('colors.gray.200') },
|
||||
'ol li::marker': {
|
||||
fontWeight: '600',
|
||||
color: theme('colors.gray.500'),
|
||||
},
|
||||
'ul li::marker': {
|
||||
backgroundColor: theme('colors.gray.500'),
|
||||
},
|
||||
strong: { color: theme('colors.gray.600') },
|
||||
blockquote: {
|
||||
color: theme('colors.gray.900'),
|
||||
borderLeftColor: theme('colors.gray.200'),
|
||||
},
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
css: {
|
||||
color: theme('colors.gray.300'),
|
||||
a: {
|
||||
color: theme('colors.primary.500'),
|
||||
'&:hover': {
|
||||
color: `${theme('colors.primary.400')} !important`,
|
||||
},
|
||||
code: { color: theme('colors.primary.400') },
|
||||
},
|
||||
h1: {
|
||||
fontWeight: '700',
|
||||
letterSpacing: theme('letterSpacing.tight'),
|
||||
color: theme('colors.gray.100'),
|
||||
},
|
||||
h2: {
|
||||
fontWeight: '700',
|
||||
letterSpacing: theme('letterSpacing.tight'),
|
||||
color: theme('colors.gray.100'),
|
||||
},
|
||||
h3: {
|
||||
fontWeight: '600',
|
||||
color: theme('colors.gray.100'),
|
||||
},
|
||||
'h4,h5,h6': {
|
||||
color: theme('colors.gray.100'),
|
||||
},
|
||||
pre: {
|
||||
backgroundColor: theme('colors.gray.800'),
|
||||
},
|
||||
code: {
|
||||
backgroundColor: theme('colors.gray.800'),
|
||||
},
|
||||
details: {
|
||||
backgroundColor: theme('colors.gray.800'),
|
||||
},
|
||||
hr: { borderColor: theme('colors.gray.700') },
|
||||
'ol li::marker': {
|
||||
fontWeight: '600',
|
||||
color: theme('colors.gray.400'),
|
||||
},
|
||||
'ul li::marker': {
|
||||
backgroundColor: theme('colors.gray.400'),
|
||||
},
|
||||
strong: { color: theme('colors.gray.100') },
|
||||
thead: {
|
||||
th: {
|
||||
color: theme('colors.gray.100'),
|
||||
},
|
||||
},
|
||||
tbody: {
|
||||
tr: {
|
||||
borderBottomColor: theme('colors.gray.700'),
|
||||
},
|
||||
},
|
||||
blockquote: {
|
||||
color: theme('colors.gray.100'),
|
||||
borderLeftColor: theme('colors.gray.700'),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/line-clamp'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
],
|
||||
}
|
||||
|
||||