@@ -1,11 +1,13 @@
11import Head from "next/head";
22import Link from "next/link";
33import { useState } from "react";
4+import classnames from "classnames";
45import Container from "../components/Container";
56import Footer from "../components/Footer";
67import Header from "../components/Header";
78import SingleColumn from "../components/SingleColumn";
89import BackgroundImage from "../components/BackgroundImage";
10+import TabCarousel from "../components/TabCarousel";
911import {
1012 getAllPosts,
1113 formatDate,
@@ -13,7 +15,53 @@ import {
1315 getGrantsTypes,
1416} from "../lib/lib";
1517
16−export default function Grants({ posts }) {
18+function addTag(array, tag) {
19+ return [...array, tag];
20+}
21+
22+function removeTag(array, tag) {
23+ const location = array.indexOf(tag);
24+ if (location === -1) {
25+ return array;
26+ }
27+ const newArr = [...array];
28+ newArr.splice(location, 1);
29+ return newArr;
30+}
31+
32+function setAllActive(fullSet) {
33+ return fullSet;
34+}
35+
36+function setAllInactive() {
37+ return [];
38+}
39+
40+function setOneTagOnly(tag) {
41+ return [tag];
42+}
43+
44+function toggleTag(array, tag) {
45+ if (array.includes(tag)) {
46+ return removeTag(array, tag);
47+ } else {
48+ return addTag(array, tag);
49+ }
50+}
51+
52+function toggleAll(currentArray, fullSet) {
53+ if (currentArray.length === 0) {
54+ return setAllActive(fullSet);
55+ } else {
56+ return setAllInactive();
57+ }
58+}
59+
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);
64+
1765 return (
1866 <Container>
1967 <SingleColumn>
@@ -21,55 +69,134 @@ export default function Grants({ posts }) {
2169 <section className="layout-wide">
2270 <div className="measure">
2371 <h2 className="pb-16">Urbit Grants</h2>
24− <p className="pb-6">
72+ <p className="">
2573 Urbit is a community project. While anyone can contribute, we help
2674 focus development and reward exceptional contribution through our
2775 grants program.
2876 </p>
29− <p>We reward contributors in four different ways:</p>
3077 </div>
3178 </section>
3279 <section className="layout-wide">
33− {posts.map((post) => {
34− return (
35− <div key={post.slug} className="mb-24 cursor-pointer">
36− <Link href={`/grants/${post.slug}`}>
37− <div>
38− {
39− // Not all blog posts have images
40− post.extra.image ? (
41− <BackgroundImage
42− src={post.extra.image}
43− style={{ height: "720px" }}
44− className="w-full rounded-lg"
45− />
46− ) : null
47− }
48− <h2 className="mt-4">{post.title}</h2>
49− <div className="flex items-baseline">
50− {post.extra.author ? (
51− <div className="type-ui text-gray mt-4">
52− {post.extra.author}
53− </div>
54− ) : null}
55− {post.extra.author && post.extra.ship ? (
56− <div className="mx-1 text-gray">•</div>
57− ) : null}
58− {post.extra.ship ? (
59− <div className="type-ui text-gray font-mono">
60− {post.extra.ship}
61− </div>
62− ) : null}
63− </div>
80+ <div className="flex items-center measure pb-12">
81+ <button
82+ onClick={() => {
83+ setTab(0);
84+ setTypes(types);
85+ }}
86+ className={`tab-button-sm mr-4 ${
87+ tab === 0 ? "text-black" : "text-gray"
88+ }`}
89+ >
90+ All
91+ </button>
92+ <button
93+ onClick={() => {
94+ setTab(1);
95+ setTypes(["propsals"]);
96+ }}
97+ className={`tab-button-sm mr-4 ${
98+ tab === 1 ? "text-black" : "text-gray"
99+ }`}
100+ >
101+ Proposals
102+ </button>
103+ <button
104+ onClick={() => {
105+ setTab(2);
106+ setTypes(["bounties"]);
107+ }}
108+ className={`tab-button-sm mr-4 ${
109+ tab === 2 ? "text-black" : "text-gray"
110+ }`}
111+ >
112+ Bounties
113+ </button>
114+ <button
115+ onClick={() => {
116+ setTab(3);
117+ setTypes(["apprenticeships"]);
118+ }}
119+ className={`tab-button-sm mr-4 ${
120+ tab === 3 ? "text-black" : "text-gray"
121+ }`}
122+ >
123+ Apprenticeships
124+ </button>
125+ <button
126+ onClick={() => {
127+ setTab(4);
128+ setTypes(["gifts"]);
129+ }}
130+ className={`tab-button-sm ${
131+ tab === 4 ? "text-black" : "text-gray"
132+ }`}
133+ >
134+ Gifts
135+ </button>
136+ </div>
64137
65− <div className="type-ui text-gray mt-2">
66− {formatDate(new Date(post.date))}
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,
144+ });
145+ return (
146+ <button
147+ onClick={() => setTags(toggleTag(activeTags, category))}
148+ className={`${activeClasses} badge-sm m-1`}
149+ >
150+ {category}
151+ </button>
152+ );
153+ })}
154+ </div>
155+
156+ {posts
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.
159+ const itemDidPass =
160+ (post.taxonomies.grant_category.some((r) =>
161+ activeTags.includes(r)
162+ ) ||
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];
168+ }
169+ return acc;
170+ }, [])
171+ .map((post) => {
172+ return (
173+ <div
174+ key={post.slug}
175+ className="mb-4 cursor-pointer bg-wall rounded-lg"
176+ >
177+ <Link href={`/grants/${post.slug}`}>
178+ <div className="p-8">
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" : ""}
186+ </p>
187+ <div className="flex">
188+ {post.taxonomies.grant_category.map((category) => (
189+ <div className="bg-gray text-wall badge-sm m-1">
190+ {category}
191+ </div>
192+ ))}
193+ </div>
194+ </div>
67195 </div>
68− </div>
69− </Link>
70− </div>
71− );
72− })}
196+ </Link>
197+ </div>
198+ );
199+ })}
73200 </section>
74201 </SingleColumn>
75202 <Footer />
@@ -80,15 +207,12 @@ export default function Grants({ posts }) {
80207export async function getStaticProps() {
81208 const categories = getGrantsCategories();
82209 const types = getGrantsTypes();
83−
84− console.log(categories, types);
85−
86− // const posts = getAllPosts(
87− // ["title", "slug", "date", "description", "extra"],
88− // "grants"
89− // );
210+ const posts = getAllPosts(
211+ ["title", "slug", "date", "description", "extra", "taxonomies"],
212+ "grants"
213+ );
90214
91215 return {
92− props: { posts: [] },
216+ props: { posts: posts, categories, types },
93217 };
94218}