@@ -1,11 +1,13 @@
1import Head from "next/head";
1import Head from "next/head";
2import Link from "next/link";
2import Link from "next/link";
3import { useState } from "react";
3import { useState } from "react";
4+import classnames from "classnames";
4import Container from "../components/Container";
5import Container from "../components/Container";
5import Footer from "../components/Footer";
6import Footer from "../components/Footer";
6import Header from "../components/Header";
7import Header from "../components/Header";
7import SingleColumn from "../components/SingleColumn";
8import SingleColumn from "../components/SingleColumn";
8import BackgroundImage from "../components/BackgroundImage";
9import BackgroundImage from "../components/BackgroundImage";
10+import TabCarousel from "../components/TabCarousel";
10 getAllPosts,
12 getAllPosts,
11 formatDate,
13 formatDate,
@@ -13,7 +15,53 @@ import {
13 getGrantsTypes,
15 getGrantsTypes,
14} from "../lib/lib";
16} from "../lib/lib";
18+function addTag(array, tag) {
19+ return [...array, tag];
22+function removeTag(array, tag) {
23+ const location = array.indexOf(tag);
24+ if (location === -1) {
27+ const newArr = [...array];
28+ newArr.splice(location, 1);
32+function setAllActive(fullSet) {
36+function setAllInactive() {
40+function setOneTagOnly(tag) {
44+function toggleTag(array, tag) {
45+ if (array.includes(tag)) {
46+ return removeTag(array, tag);
48+ return addTag(array, tag);
52+function toggleAll(currentArray, fullSet) {
53+ if (currentArray.length === 0) {
54+ return setAllActive(fullSet);
56+ return setAllInactive();
16−export default function Grants({ posts }) {
60+export default function Grants({ posts, categories, types }) {
61+ const [activeTags, setTags] = useState([]);
62+ const [activeTypes, setTypes] = useState(types);
63+ const [tab, setTab] = useState(0);
18 <Container>
66 <Container>
19 <SingleColumn>
67 <SingleColumn>
@@ -21,55 +69,134 @@ export default function Grants({ posts }) {
21 <section className="layout-wide">
69 <section className="layout-wide">
22 <div className="measure">
70 <div className="measure">
23 <h2 className="pb-16">Urbit Grants</h2>
71 <h2 className="pb-16">Urbit Grants</h2>
24− <p className="pb-6">
72+ <p className="">
25 Urbit is a community project. While anyone can contribute, we help
73 Urbit is a community project. While anyone can contribute, we help
26 focus development and reward exceptional contribution through our
74 focus development and reward exceptional contribution through our
27 grants program.
75 grants program.
29− <p>We reward contributors in four different ways:</p>
31 </section>
78 </section>
32 <section className="layout-wide">
79 <section className="layout-wide">
33− {posts.map((post) => {
35− <div key={post.slug} className="mb-24 cursor-pointer">
36− <Link href={`/grants/${post.slug}`}>
39− // Not all blog posts have images
42− src={post.extra.image}
43− style={{ height: "720px" }}
44− className="w-full rounded-lg"
48− <h2 className="mt-4">{post.title}</h2>
49− <div className="flex items-baseline">
80+ <div className="flex items-center measure pb-12">
50− {post.extra.author ? (
81+ <button
51− <div className="type-ui text-gray mt-4">
82+ onClick={() => {
52− {post.extra.author}
83+ setTab(0);
53− </div>
84+ setTypes(types);
55− {post.extra.author && post.extra.ship ? (
86+ className={`tab-button-sm mr-4 ${
56− <div className="mx-1 text-gray">•</div>
87+ tab === 0 ? "text-black" : "text-gray"
58− {post.extra.ship ? (
89+ >
59− <div className="type-ui text-gray font-mono">
90+ All
60− {post.extra.ship}
91+ </button>
62− ) : null}
93+ onClick={() => {
95+ setTypes(["propsals"]);
97+ className={`tab-button-sm mr-4 ${
98+ tab === 1 ? "text-black" : "text-gray"
106+ setTypes(["bounties"]);
108+ className={`tab-button-sm mr-4 ${
109+ tab === 2 ? "text-black" : "text-gray"
117+ setTypes(["apprenticeships"]);
119+ className={`tab-button-sm mr-4 ${
120+ tab === 3 ? "text-black" : "text-gray"
128+ setTypes(["gifts"]);
130+ className={`tab-button-sm ${
131+ tab === 4 ? "text-black" : "text-gray"
138+ <div className="flex flex-wrap mb-12">
139+ {categories.map((category) => {
140+ const isActive = activeTags.includes(category);
141+ const activeClasses = classnames({
142+ "bg-green text-white": isActive,
143+ "bg-wall text-black": !isActive,
147+ onClick={() => setTags(toggleTag(activeTags, category))}
148+ className={`${activeClasses} badge-sm m-1`}
157+ .reduce((acc, post) => {
158+ // Posts are returned if they match both the selected category and selected tags, or if the user has no category filters set.
160+ (post.taxonomies.grant_category.some((r) =>
161+ activeTags.includes(r)
163+ activeTags.length === 0) &&
164+ post.taxonomies.grant_type.some((r) => activeTypes.includes(r));
165+ // Return the item if the above check has passed
166+ if (itemDidPass === true) {
167+ return [...acc, post];
175+ className="mb-4 cursor-pointer bg-wall rounded-lg"
177+ <Link href={`/grants/${post.slug}`}>
65− <div className="type-ui text-gray mt-2">
178+ <div className="p-8">
66− {formatDate(new Date(post.date))}
179+ <h3 className="type-ui mb-4">{post.title}</h3>
180+ <p className="mb-4">{post.extra.description}</p>
181+ <div className="flex w-full items-center justify-between">
182+ <p className="text-gray">
183+ <strong>Reward: </strong>
184+ {post.extra.reward} star
185+ {post.extra.reward > 1 ? "s" : ""}
187+ <div className="flex">
188+ {post.taxonomies.grant_category.map((category) => (
189+ <div className="bg-gray text-wall badge-sm m-1">
73 </section>
200 </section>
74 </SingleColumn>
201 </SingleColumn>
75 <Footer />
202 <Footer />
@@ -80,15 +207,12 @@ export default function Grants({ posts }) {
80export async function getStaticProps() {
207export async function getStaticProps() {
81 const categories = getGrantsCategories();
208 const categories = getGrantsCategories();
82 const types = getGrantsTypes();
209 const types = getGrantsTypes();
84− console.log(categories, types);
86− // const posts = getAllPosts(
210+ const posts = getAllPosts(
87− // ["title", "slug", "date", "description", "extra"],
211+ ["title", "slug", "date", "description", "extra", "taxonomies"],
88− // "grants"
212+ "grants"
92− props: { posts: [] },
216+ props: { posts: posts, categories, types },