@@ -11,17 +11,20 @@ import {
13 getPostBySlug,
13 getPostBySlug,
14− getAllPosts,
14+ getPage,
15 formatDate,
15 formatDate,
16} from "@urbit/foundation-design-system";
16} from "@urbit/foundation-design-system";
17import Header from "../../components/Header";
17import Header from "../../components/Header";
18import Footer from "../../components/Footer";
18import Footer from "../../components/Footer";
19+import MetadataBlock from "../../components/ecosystem/MetadataBlock";
20+import ResourceCard from "../../components/gateway/ResourceCard";
21+import JoinGroup from "../../components/JoinGroup";
19import ob from "urbit-ob";
22import ob from "urbit-ob";
20import { DateTime } from "luxon";
23import { DateTime } from "luxon";
21import fs from 'fs';
24import fs from 'fs';
22−import { join } from 'path';
25+import path, { join } from 'path';
24−export default function Grant({ post, markdown, search }) {
27+export default function Grant({ post, markdown, match, search }) {
25 const router = useRouter();
28 const router = useRouter();
26 if (!router.isFallback && !post?.slug) {
29 if (!router.isFallback && !post?.slug) {
27 return <ErrorPage />;
30 return <ErrorPage />;
@@ -65,6 +68,44 @@ export default function Grant({ post, markdown, search }) {
74+ } else if (post?.extra?.completed) {
76+ } else if (post?.extra?.canceled) {
79+ status = "In Progress"
81+ const statusBadge = <div className={
82+ classnames("badge-sm mr-1 my-1", {
83+ "bg-red": status === "Canceled",
84+ "bg-green-400 text-white": status === "Completed" || status === "Open",
85+ "bg-wall-300": status === "In Progress",
90+ let deliverable = null;
91+ if (post.extra.deliverable) {
93+ deliverable = <ResourceCard
94+ title={match.data.title}
95+ image={match.data.image}
96+ color={match.data.bgColor || "#000000"}
97+ description={match.data?.description || ""}
102+ else if (post.extra.deliverable.startsWith("~")) {
103+ deliverable = <JoinGroup emphasize groupName={post.extra.deliverable} />
105+ deliverable = <a className="font-medium text-green-400" href={post.extra.deliverable}>{post.extra.deliverable}</a>
68 const metaPost = Object.assign({}, post);
109 const metaPost = Object.assign({}, post);
69 metaPost.title = `${post.title} - Grant`;
110 metaPost.title = `${post.title} - Grant`;
@@ -79,38 +120,8 @@ export default function Grant({ post, markdown, search }) {
79 <Header search={search} />
120 <Header search={search} />
80 <Section narrow short>
121 <Section narrow short>
81 <h1>{post.title}</h1>
122 <h1>{post.title}</h1>
82− {post.extra.assignee ? (
83− <div className="type-ui text-wall-500 mt-4">
84− Grantee(s): {assigneeLink}
87− {post.extra.mentor ? (
88− <div className="type-ui text-wall-500 mt-4">
92− {post.extra.champion ? (
93− <div className="type-ui text-wall-500 mt-4">
94− Champion: {mentorLink}
98− <Link href={`/ids/${post.extra.ship}`} passHref>
99− <a className="type-sub-bold text-wall-500 font-mono">
104− <div className="type-ui text-wall-500 mt-4 md:mt-8 lg:mt-10 flex items-center space-x-1">
105− {formatDate(DateTime.fromISO(post.date))}
106− {post?.extra?.grant_id ? (
108− <p className="ml-1 text-wall-500 type-ui">• ID:</p>
109− <p className="font-mono type-ui"> {post.extra.grant_id}</p>
113− <div className="flex items-center flex-wrap mt-4 md:mt-8 lg:mt-10">
123+ <div className="flex items-center flex-wrap my-4">
114 {post.taxonomies.grant_type.map((category) => {
125 {post.taxonomies.grant_type.map((category) => {
115 const className = classnames({
126 const className = classnames({
116 "bg-blue-400 text-white": category === "Proposal",
127 "bg-blue-400 text-white": category === "Proposal",
@@ -129,6 +140,46 @@ export default function Grant({ post, markdown, search }) {
143+ <div className="flex flex-wrap md:flex-nowrap justify-between">
144+ {post?.extra?.grant_id ? (
148+ content={post.extra.grant_id}
154+ content={formatDate(DateTime.fromISO(post.date))}
156+ {post.extra.mentor ? (
159+ content={mentorLink}
162+ {post.extra.champion ? (
165+ content={mentorLink}
168+ {post.extra.assignee ? (
171+ content={assigneeLink}
175+ {post?.extra?.link?.startsWith("~") && <div className="flex flex-col my-4 space-y-2">
176+ <p className="text-wall-400 font-semibold">Group</p>
177+ <JoinGroup emphasize groupName={post.extra.link} />
179+ {deliverable && <div className="flex flex-col my-4 space-y-2">
180+ <p className="text-wall-400 font-semibold">Deliverable</p>
132 </Section>
183 </Section>
133 <Section narrow className="markdown">
184 <Section narrow className="markdown">
134 <Markdown.render content={JSON.parse(markdown)} />
185 <Markdown.render content={JSON.parse(markdown)} />
@@ -149,6 +200,7 @@ export default function Grant({ post, markdown, search }) {
151export async function getStaticProps({ params }) {
202export async function getStaticProps({ params }) {
203+ // Gets post in any year folder sharing the slug name in the param
152 const basePath = join(process.cwd(), 'content/grants')
204 const basePath = join(process.cwd(), 'content/grants')
153 const years = fs.readdirSync(basePath, { 'withFileTypes': true }).filter((f) => f.isDirectory());
205 const years = fs.readdirSync(basePath, { 'withFileTypes': true }).filter((f) => f.isDirectory());
154 const dir = years.find((year) => {
206 const dir = years.find((year) => {
@@ -162,8 +214,23 @@ export async function getStaticProps({ params }) {
164 const markdown = JSON.stringify(Markdown.parse({ post }));
216 const markdown = JSON.stringify(Markdown.parse({ post }));
218+ // Matches deliverable to page
220+ if (post?.extra?.deliverable?.startsWith("~")) {
221+ const ship = post.extra.deliverable.split("/")[0];
222+ const application = post.extra.deliverable.split("/")[1];
223+ const fileExists = fs.existsSync(path.join(process.cwd(), "content/applications", ship, `${application}.md`));
224+ match = fileExists ? getPage(
227+ "content/applications",
228+ post.extra.deliverable
166− props: { post, markdown },
233+ props: { post, markdown, match },