on master help sign in
sorting interface for grants
commit692aea326e5200bfc49bc445c90e15838b16b7ac authorGavin Atkinson <gvnatknsn@protonmail.com> dateWed 23 Jun 2021, 14:47 (5 years ago) parentd3438c4
10 files changed+219−103
content/grants/apprenticeships/_index.md−4 content/grants/bounties/_index.md−4 content/grants/gifts/_index.md−4 content/grants/proposals/_index.md−4 lib/lib.js+5−14 pages/events.js−1 pages/grants.js+173−49 pages/index.js+10−6 styles/globals.css+13 tailwind.config.js+18−17
content/grants/apprenticeships/_index.mddeleted−4
@@ -1,4 +0,0 @@
1−+++
2−title = "Apprenticeships"
3−template = "grant_type/apprenticeships.html"
4−+++
content/grants/bounties/_index.mddeleted−4
@@ -1,4 +0,0 @@
1−+++
2−title = "Bounties"
3−template = "grant_type/bounties.html"
4−+++
content/grants/gifts/_index.mddeleted−4
@@ -1,4 +0,0 @@
1−+++
2−title = "Gifts"
3−template = "grant_type/gifts.html"
4−+++
content/grants/proposals/_index.mddeleted−4
@@ -1,4 +0,0 @@
1−+++
2−title = "Proposals"
3−template = "grant_type/proposals.html"
4−+++
lib/lib.js+5−14
@@ -228,15 +228,15 @@ export function getGrantsTypes() {
228228 const uniqueTypes = paths
229229 // Get only markdown files
230230 .filter((path) => parse(path).ext === ".md")
231− // Parse frontmatter
231+ // Parse frontmatter to JSON
232232 .map((path) =>
233233 matter(fs.readFileSync(join(directories.grants, path)), options)
234234 )
235− // Each markdown file has an array of categories. Collapse these arrays into one array.
235+ // Each markdown file has an array of types. Collapse these arrays into one array.
236236 .reduce((acc, metadata) => {
237237 return [...acc, ...metadata.data.taxonomies.grant_type];
238238 }, [])
239− // Deduplicate single array of categories such that the output is an array where each category is represented only once.
239+ // Deduplicate single array of types such that the output is an array where each type is represented only once.
240240 .reduce((acc, uniqueItem) => {
241241 if (acc.includes(uniqueItem)) {
242242 return acc;
@@ -246,12 +246,13 @@ export function getGrantsTypes() {
246246 return uniqueTypes;
247247}
248248
249+// Returns a number which represents how many grants are open, or uncompleted
249250export function getOpenGrantsCount() {
250251 const paths = fs.readdirSync(directories.grants);
251252 const count = paths
252253 // Get only markdown files
253254 .filter((path) => parse(path).ext === ".md")
254− // Parse frontmatter
255+ // Parse frontmatter to JSON
255256 .map((path) =>
256257 matter(fs.readFileSync(join(directories.grants, path)), options)
257258 )
@@ -264,13 +265,3 @@ export function getOpenGrantsCount() {
264265 }, 0);
265266 return count;
266267}
267−
268−/*
269− * types = an array of strings
270− */
271−export function getGrantsByCategories(types) {}
272−
273−/*
274− * types = an array of strings
275− */
276−export function getGrantsByTypes(types) {}
pages/events.js−1
@@ -69,7 +69,6 @@ function EventCard(props) {
6969}
7070
7171export default function Events({ pastEvents, currentEvents }) {
72− console.log(currentEvents);
7372 return (
7473 <Container>
7574 <SingleColumn>
pages/grants.js+173−49
@@ -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}
pages/index.js+10−6
@@ -60,9 +60,11 @@ export default function Home({ posts, events, openGrantsCount }) {
6060 <div className="flex flex-col md:flex-row md:items-center measure pb-12">
6161 <h2 className="m-0 p-0 mr-4 md:mb-0 mb-4">Urbit Grants</h2>
6262 <div>
63− <button className="bg-green text-white badge-lg">
64− {openGrantsCount} Open
65− </button>
63+ <Link href="/grants">
64+ <a className="bg-green text-white badge-lg">
65+ {openGrantsCount} Open
66+ </a>
67+ </Link>
6668 </div>
6769 </div>
6870 <div className="flex flex-col md:flex-row w-full items-center bg-washedGreen px-8 py-8 rounded-xl">
@@ -88,9 +90,11 @@ export default function Home({ posts, events, openGrantsCount }) {
8890 We regularly give away address space to reward community
8991 contributions, including education, promotion, and development.
9092 </p>
91− <button className="button-lg bg-green text-white">
92− View Grants
93− </button>
93+ <div className="table">
94+ <Link href="/grants">
95+ <a className="button-lg bg-green text-white">View Grants</a>
96+ </Link>
97+ </div>
9498 </div>
9599 </section>
96100
styles/globals.css+13
@@ -49,6 +49,10 @@ h4,
4949 @apply text-base md:text-lg lg:text-xl font-semibold;
5050}
5151
52+.type-ui-small {
53+ @apply text-sm lg:text-base font-medium;
54+}
55+
5256p,
5357.type-p {
5458 @apply text-base md:text-lg lg:text-xl;
@@ -79,6 +83,10 @@ button {
7983 @apply flex px-4 lg:px-6 py-2 rounded-lg h-12 md:h-16 flex-center type-ui;
8084}
8185
86+.badge-sm {
87+ @apply flex type-ui-small px-2 lg:px-3 items-center justify-center h-8 md:h-10 rounded-full;
88+}
89+
8290.badge-lg {
8391 @apply flex type-ui px-4 lg:px-6 items-center justify-center h-12 md:h-16 rounded-full;
8492}
@@ -88,6 +96,11 @@ button {
8896 overflow: visible;
8997}
9098
99+.tab-button-sm {
100+ @apply type-ui h-12;
101+ overflow: visible;
102+}
103+
91104.button-sm:hover {
92105 opacity: 0.66;
93106}
tailwind.config.js+18−17
@@ -1,29 +1,30 @@
11module.exports = {
2− purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
2+ purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
33 darkMode: false, // or 'media' or 'class'
44 theme: {
55 colors: {
6− black: '#000',
7− white: '#FFF',
8− gray: 'rgba(0, 0, 0, 0.6)',
9− lightGray: 'rgba(0, 0, 0, 0.2)',
10− wall: 'rgba(244, 243, 241, 1)',
11− yellow: '#FCDB63',
12− midYellow: 'rgba(255, 199, 0, 0.6)',
13− lightYellow: 'rgba(255, 199, 0, 0.3)',
14− washedYellow: 'rgba(255, 199, 0, 0.05)',
15− green: 'rgba(0, 177, 113, 1)',
16− midGreen: 'rgba(0, 159, 101, 0.6)',
17− lightGreen: 'rgba(0, 159, 101, 0.3)',
18− washedGreen: 'rgba(0, 159, 101, 0.05)',
6+ black: "#000",
7+ white: "#FFF",
8+ gray: "rgba(0, 0, 0, 0.6)",
9+ lightGray: "rgba(0, 0, 0, 0.2)",
10+ wall: "rgba(244, 243, 241, 1)",
11+ yellow: "#FCDB63",
12+ midYellow: "rgba(255, 199, 0, 0.6)",
13+ lightYellow: "rgba(255, 199, 0, 0.3)",
14+ washedYellow: "rgba(255, 199, 0, 0.05)",
15+ green: "rgba(0, 177, 113, 1)",
16+ midGreen: "rgba(0, 159, 101, 0.6)",
17+ lightGreen: "rgba(0, 159, 101, 0.3)",
18+ washedGreen: "rgba(0, 159, 101, 0.05)",
19+ blue: "#219DFF",
1920 },
2021 fontFamily: {
21− sans: ['Inter', '-apple-system', 'sans-serif'],
22− mono: ['Source Code Pro', 'monospace'],
22+ sans: ["Inter", "-apple-system", "sans-serif"],
23+ mono: ["Source Code Pro", "monospace"],
2324 },
2425 },
2526 variants: {
2627 extend: {},
2728 },
2829 plugins: [],
29−}
30+};