on master help sign in
grants: add RFP type and initial content, show in-progress grants (#2056)
0411ea4Josh Lehman
components/GrantPreview.js+2−1
@@ -8,7 +8,7 @@ export default function GrantPreview({ grant }) {
8
8
9 const className = classnames({
9 const className = classnames({
10 "bg-lightBlue": type.includes("Proposal") && isOpen,
10 "bg-lightBlue": type.includes("Proposal") && isOpen,
11− "bg-green-200": type.includes("Apprenticeship") && isOpen,
11+ "bg-purple-200": type.includes("RFP") && isOpen,
12 "bg-yellow-100": type.includes("Bounty") && isOpen,
12 "bg-yellow-100": type.includes("Bounty") && isOpen,
13 });
13 });
14
14
@@ -38,6 +38,7 @@ export default function GrantPreview({ grant }) {
38 {grant.taxonomies.grant_type.map((category) => {
38 {grant.taxonomies.grant_type.map((category) => {
39 const className = classnames({
39 const className = classnames({
40 "bg-blue-400 text-white": category === "Proposal",
40 "bg-blue-400 text-white": category === "Proposal",
41+ "bg-purple text-white": category === "RFP",
41 "bg-green-400 text-white": category === "Apprenticeship",
42 "bg-green-400 text-white": category === "Apprenticeship",
42 "bg-yellow-300": category === "Bounty",
43 "bg-yellow-300": category === "Bounty",
43 });
44 });
content/grants/2022/chess-bounty.md+1−1
@@ -20,7 +20,7 @@ work_request_link = "https://airtable.com/shr4qt9t9kz7RaOIa?prefill_Grant+ID=B01
20+++
20+++
21
21
22# Rationale
22# Rationale
23−~finmep-lanteb is sponsoring the improvement of the Chess app to create an Urbit Chess experience comparable to that of any modern Chess website. The current Chess app meets the basic requirements to play a game with a friend, but lacks various quality of life features: move notifications, undo requests, %pals integration, etc. In addition, only unrated, correspondence games are currently possible. These missing features & improvements offer a great opportunity for a team of 3 novice Urbit developers (preferably three Hoon School Live and App School Live graduates) to work together to complete this bounty, each earning a star in the process.
23+[Omitted long matching line]
24
24
25The goals of this bounty are to resolve the quality of life issues present in the Chess app, as well as enable timed and rated games.
25The goals of this bounty are to resolve the quality of life issues present in the Chess app, as well as enable timed and rated games.
26
26
content/grants/2022/digital-storefront.md+2−2
@@ -63,9 +63,9 @@ Shop:
63 ==
63 ==
64```
64```
65## User Stories
65## User Stories
66−- John Murphy is a social media influencer who wants to sell his jazz album on the web. He looked at selling it using Shopify, but was turned off at the idea that he'd be dependent on Shopify and wouldn't be able to export his data if he ever wanted to move to a different webstore provider. John wants an online store solution that allows him to run the webstore himself on the server at his house without having to fiddle with it. John decides to download Tirrel Studio, because he heard they just added online store functionality. He sets up his website, adds his product details, decides how much it should cost, and then requests to be a merchant through their payment processor. John feels good knowing that he owns all of his shop's data and software, and that his only external dependency is on a payment processor that he could switch out for a different one.
66+[Omitted long matching line]
67
67
68−- Timothy Gunn is an online conspiracy theorist who writes articles about the new world order. He has an avid online following, but sometimes his posts get too juicy for conventional platforms. He doesn't trust Substack and wants to host his newsletter himself but still get paid. His audience is a little too normie to use Bitcoin, and Bitcoin isn't that convenient for payments these days anyway. Timothy sets up his newsletter site via Studio, imports his mailing list from his old Mailchimp newsletter, requests to be a merchant, and then adds pay-walls in front of his best posts. Now he's ready to make money on his own terms.
68+[Omitted long matching line]
69
69
70- Jimmy Takeria is a big calisthenics guy who teaches other guys how to get jacked. He makes informational videos and posts them to YouTube as well as running a Discord community about calisthenics. Jimmy doesn't like YouTube and Discord's new misinformation policies because he has unconventional ideas about nutrition. Jimmy decides to move his community off of Discord and into Groups on Urbit, and sets up a Studio webstore where he sells access to his latest workout videos and cutting edge nutritional info on a subscription basis. He's satisfied that he can manage his entire online business using Urbit.
70- Jimmy Takeria is a big calisthenics guy who teaches other guys how to get jacked. He makes informational videos and posts them to YouTube as well as running a Discord community about calisthenics. Jimmy doesn't like YouTube and Discord's new misinformation policies because he has unconventional ideas about nutrition. Jimmy decides to move his community off of Discord and into Groups on Urbit, and sets up a Studio webstore where he sells access to his latest workout videos and cutting edge nutritional info on a subscription basis. He's satisfied that he can manage his entire online business using Urbit.
71
71
content/grants/2023/network-weather.mdnew file+24
@@ -0,0 +1,24 @@
1++++
2+
3+title = "Network Weather Simulator for %pyro/%aqua"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["Core Dev"]
9+
10+[extra]
11+image = ""
12+description = "Network Weather Simulator for `%pyro`/`%aqua"
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1004"
17+completed = false
18+canceled = false
19++++
20+
21+`%pyro` and `%aqua` are virtualization tools that allow developers to run one or many virtual ships within their ship. This opens up many testing possibilities, since it's possible to spin up an entire fleet of virtual ships with state and orchestrate them within your ship. But since all of hte ships run one one computer, the network conditions are unrealistically good. In the real world, network connections fail, time out and generally perform in unexpected ways.
22+
23+Write a library that allows developers to easily add realistic "network weather" to fleet testing in `%pyro`/`%aqua`
24+
content/grants/2023/pastebin.mdnew file+24
@@ -0,0 +1,24 @@
1++++
2+
3+title = "Pastebin on Urbit"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["App Dev"]
9+
10+[extra]
11+image = ""
12+description = "Share plaintext snippets on Urbit"
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1000"
17+completed = false
18+canceled = false
19+
20++++
21+
22+Tools like pastebin and Github gists allow users to easily share plain text snippets. This is useful for sharing code or simple writeups.
23+
24+Implement minimal plaintext snippet sharing for Urbit, with support for both public (anyone with the path can read the snippet) and private (only invited individuals can read snippets).
content/grants/2023/text-search.mdnew file+24
@@ -0,0 +1,24 @@
1++++
2+
3+title = "Generic Text Search Tool for Urbit"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["Core Dev"]
9+
10+[extra]
11+image = ""
12+description = "Build Urbit's answer to grep."
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1002"
17+completed = false
18+canceled = false
19+
20++++
21+
22+Text search is a key part of many different use cases, but Urbit currently has no equivalent to `grep`, the general purpose GNU search tool.
23+
24+Build Urbit's answer to `grep`.
content/grants/2023/tinygrad.mdnew file+24
@@ -0,0 +1,24 @@
1++++
2+
3+title = "Tinygrad ML Library in Hoon"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["Core Dev"]
9+
10+[extra]
11+image = ""
12+description = "Implement a small machine learning library (and jets!) in hoon."
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1003"
17+completed = false
18+canceled = false
19+
20++++
21+
22+Geohot has published the [Tinygrad library](https://github.com/geohot/tinygrad) for doing basic machine learning. This library is small and simple which makes it a potentially good target for jetting. Doing so would give basic ML functionality in Urbit.
23+
24+Implement Tinygrad (with jets) in Urbit
content/grants/2023/treesitter.mdnew file+24
@@ -0,0 +1,24 @@
1++++
2+
3+title = "Treesitter Hoon Parser"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["Core Dev"]
9+
10+[extra]
11+image = ""
12+description = "Create a better hoon parser with Treesitter"
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1001"
17+completed = false
18+canceled = false
19+
20++++
21+
22+[Tree-sitter](https://github.com/tree-sitter/tree-sitter) represents the state of the art for multi-language parsing. It is used by Atom and Github to parse any language, on every keystroke, in an editor.
23+
24+Use tree-sitter to write a superior hoon parser.
content/grants/2023/update-mass.mdnew file+26
@@ -0,0 +1,26 @@
1++++
2+
3+title = "Modernize |mass"
4+date = "2023-03-02"
5+
6+[taxonomies]
7+grant_type = ["RFP"]
8+grant_category = ["Core Dev"]
9+
10+[extra]
11+image = ""
12+description = "Update the |mass utility to reinject the output into arvo from the runtime."
13+reward = "TBD"
14+assignee = [""]
15+champion = [""]
16+grant_id = "P1001"
17+completed = false
18+canceled = false
19+
20++++
21+
22+`|mass` is a utility for measuring a pier's resource usage. It is used for debugging and development. Currently, the output of this utility is a mixture of data from within arvo and from the runtime. This heterogeneity means that what is produced is not a noun but interleaved print statements to the terminal.
23+
24+Once `|mass` produces a noun, it can be sent to other ships over `%ames` for bug reports, collected in a central location or analyzed for further action by arvo.
25+
26+Update the `|mass` utility to reinject the output into arvo from the runtime.
content/privacy.md+1−1
@@ -76,7 +76,7 @@ We do not knowingly collect, maintain, or use personal information from children
76
76
77### International visitors
77### International visitors
78
78
79−The Services are hosted in the United States and are intended for visitors located within the United States. If you choose to use the Services from the European Union or other regions of the world with laws governing data collection and use that may differ from U.S. law, then please note that you are transferring your personal information outside of those regions to the United States for storage and processing. Also, we may transfer your data from the U.S. to other countries or regions in connection with storage and processing of data, fulfilling your requests, and operating the Services. By providing any information, including personal information, on or to the Services, you consent to such transfer, storage, and processing.
79+[Omitted long matching line]
80
80
81### Changes to this Privacy Policy
81### Changes to this Privacy Policy
82
82
content/rfps.mdnew file+40
@@ -0,0 +1,40 @@
1++++
2+title = "Requests for Proposals"
3+date = "2023-03-02"
4+weight = 1
5+description = "Everything you need to know about fulfilling a request for proposal or RFP"
6++++
7+
8+The imagination of people building on Urbit often outpaces the available development hours. For this case, we have Requests for Proposals (RFPs).
9+
10+These are ideas that the community would like to see come to fruition, but whose details haven't been completely ironed out. Since they're not fully fleshed out, they're a great opportunity for contributors to add their own ideas and creative approaches, while learning about developing and shipping on Urbit.
11+
12+If any of these RFPs sound compelling, we encourage you to come by the [Urbit Grants office hours](https://app.gather.town/app/xAYeiPI2XDYhRM9t/urbit-hacker-house?spawnToken=ufV1APwWTGmYVTgzyx0z) or DM [~poldec-tonteg](https://urbit.org/ids/~poldec-tonteg) to discuss drafting and submitting a proposal.
13+
14+# Jump in
15+
16+The Hacker House is a UF-hosted gather.town instance where contributors hang out, pair program and discuss all things Urbit. At the Office Hours, members from the UF team can help refine the Request idea and give you context about what needs to be built, potential use cases and architectural precedents in the codebase.
17+
18+Once you've got your mind around this, the next step is to draft the Proposal
19+
20+# Drafting a proposal
21+
22+All of the best practices outlined on the [Proposals](https://urbit.org/grants/proposals) section of the website apply equally to Proposals that respond to a Request.
23+
24+Good proposals all include the following:
25+
26+- A **detailed and clear description of the proposal**. If you're proposing something technical, user stories are a good idea.
27+- An overview of **why you are the right person for the job**. A description of your background, familiarity with the project, and professional/education experience are all good starts.
28+- Your estimate for **date of completion**.
29+- The **amount of funding** you'd like for the project, denominated in stars.
30+- What **specific deliverables** will look like.
31+
32+It's recommended to break your project into _milestones_, each of which must have its own completion dates, funding amounts and deliverables. In general, proposals should target a first deliverable within two months of the start of the project. Proposals should have a maximum of five milestones as scoping a project beyond that is impractical, and each milestone should constitute significant enough work to warrant the reward of a full star.
33+
34+Feel free to come to multiple office hours or connect on network to develop the proposal further. Once it's in a good state, submit it through [this form](https://airtable.com/shrCi54rEDxgSZr3z). Submitted proposals are then reviewed internall twice a week by the full grants team.
35+
36+The main distinction between a self-originating Proposal and one that responds to a Request is that the latter doesn't need to go through the further step of getting a champion, as the originator of the Request will fulfill that role.
37+
38+# Getting paid
39+
40+A good proposal will have clear success criteria for each milestone. At the completion of each milestone, submit an invoice to the UF for that milestones star compensation and your champion will have the opportunity to review work.
content/updates/2018-7-30-update.md+1−1
@@ -53,7 +53,7 @@ latest backend Landscape API's that _Isaac_ `~davtyr-nimren` has been improving.
53Urbit is becoming more usable by the day.
53Urbit is becoming more usable by the day.
54
54
55Last but not least, the Documentation team continued doing `~zod`'s work helping
55Last but not least, the Documentation team continued doing `~zod`'s work helping
56−teach Urbit to the world. _Josh_ `~taglux-nidsep` extended his new Hoon docs to cover Hoon cores, an introduction to the Hoon standard library, and a couple of Hoon examples with very detailed walkthrough commentary (drafts [here](https://github.com/joshuareagan/doc-drafts)). And _Robert_ `~lodleb-ritrul` extended the current Hoon-standard-library documentation on random atom generation and atom scrambling, as well as the Hoon cores for computing modular and signed-integer arithmetic (`urbit/docs` pull requests [here](https://github.com/urbit/docs/pulls)). Very soon you'll actually be able to understand how Urbit works.
56+[Omitted long matching line]
57
57
58Also: we'll be hosting another party this month on Friday, August 17th. Save the date! Link: [meetup.com: Urbit IRL ~2018.8](https://www.meetup.com/urbit-sf/events/253292678/)
58Also: we'll be hosting another party this month on Friday, August 17th. Save the date! Link: [meetup.com: Urbit IRL ~2018.8](https://www.meetup.com/urbit-sf/events/253292678/)
59
59
content/updates/2019-2-11-update.md+1−1
@@ -26,7 +26,7 @@ To stay up to date on network status, follow [@zodisok](https://twitter.com/zodi
26
26
27## Team
27## Team
28
28
29−_Ted_ `~rovnys-ricfer` and _Joe_ `~master-morzod` got an `%ames` rewrite to compile and are working on documentation and unit tests. _Elliot_ `~littel-ponys` is rewriting `%eyre`, our webserver, which is some of the gnarliest code in Arvo. Outbound web requests are now working, which has allowed us to delete old `%eyre` from the [lighter-than-eyre branch](https://github.com/urbit/arvo/tree/lighter-than-eyre), and work continues porting individual `%gall` applications over to `%light`. We’re looking forward to moving Landscape onto new `%eyre` soon. _Logan_ `~tacryt-socryp` is rewriting the HTTP client in Vere (Arvo’s interpreter) to better support HTTP/2. This will allow us to provide push notifications in the heretofore rumored Landscape iOS app. Finally, _Gavin_ `ridlur-figbud` and _Jimmy_ `fallyn-balfus` are working on a new design system to enable fast UI prototyping. We’re very excited to share more about that in the near future!
29+[Omitted long matching line]
30
30
31Shout-out to _Philip_ `wicdev-wisryt` for [this heroic weekend PR](https://github.com/urbit/arvo/pull/1072) that fixed persistent connectivity issues on the network after [Thursday’s breach](https://twitter.com/zodisok/status/1096225302210957312).
31Shout-out to _Philip_ `wicdev-wisryt` for [this heroic weekend PR](https://github.com/urbit/arvo/pull/1072) that fixed persistent connectivity issues on the network after [Thursday’s breach](https://twitter.com/zodisok/status/1096225302210957312).
32
32
package-lock.json+1−1
@@ -12,7 +12,7 @@
12 "@socialgouv/matomo-next": "^1.3.0",
12 "@socialgouv/matomo-next": "^1.3.0",
13 "@tailwindcss/aspect-ratio": "^0.4.0",
13 "@tailwindcss/aspect-ratio": "^0.4.0",
14 "@tlon/sigil-js": "^1.4.5",
14 "@tlon/sigil-js": "^1.4.5",
15− "@urbit/foundation-design-system": "0.7.1",
15+ "@urbit/foundation-design-system": "0.7.3",
16 "@urbit/markdoc": "^0.1.4",
16 "@urbit/markdoc": "^0.1.4",
17 "axios": "^0.26.1",
17 "axios": "^0.26.1",
18 "buffer": "^6.0.3",
18 "buffer": "^6.0.3",
package.json+1−1
@@ -20,7 +20,7 @@
20 "@socialgouv/matomo-next": "^1.3.0",
20 "@socialgouv/matomo-next": "^1.3.0",
21 "@tailwindcss/aspect-ratio": "^0.4.0",
21 "@tailwindcss/aspect-ratio": "^0.4.0",
22 "@tlon/sigil-js": "^1.4.5",
22 "@tlon/sigil-js": "^1.4.5",
23− "@urbit/foundation-design-system": "0.7.1",
23+ "@urbit/foundation-design-system": "0.7.3",
24 "@urbit/markdoc": "^0.1.4",
24 "@urbit/markdoc": "^0.1.4",
25 "axios": "^0.26.1",
25 "axios": "^0.26.1",
26 "buffer": "^6.0.3",
26 "buffer": "^6.0.3",
pages/grants.js+70−52
@@ -33,7 +33,7 @@ export default function Grants({ posts, categories, types, search }) {
33 const router = useRouter();
33 const router = useRouter();
34 let { status, type, category } = router.query;
34 let { status, type, category } = router.query;
35 if (status === undefined) {
35 if (status === undefined) {
36− status = "open";
36+ status = ["open", "wip"];
37 }
37 }
38
38
39 function push(toQuery) {
39 function push(toQuery) {
@@ -82,8 +82,8 @@ export default function Grants({ posts, categories, types, search }) {
82 const byStatus = (post) => {
82 const byStatus = (post) => {
83 return (
83 return (
84 (isOrIsIn("open", status) ? post.status === "open" : false) ||
84 (isOrIsIn("open", status) ? post.status === "open" : false) ||
85− (isOrIsIn("complete", status) ? post.status === "completed" : false) ||
85+ (isOrIsIn("complete", status) ? post.status === "completed" : false) ||
86− (isOrIsIn("wip", status) ? post.status === "wip" : false)
86+ (isOrIsIn("wip", status) ? post.status === "wip" : false)
87 );
87 );
88 };
88 };
89
89
@@ -91,10 +91,10 @@ export default function Grants({ posts, categories, types, search }) {
91
91
92 const filteredPosts = postsByStatus.filter((post) => {
92 const filteredPosts = postsByStatus.filter((post) => {
93 const hasCategory = category
93 const hasCategory = category
94− ? isArray(category)
94+ ? isArray(category)
95− ? post.taxonomies.grant_category.some((cat) => category.includes(cat))
95+ ? post.taxonomies.grant_category.some((cat) => category.includes(cat))
96− : post.taxonomies.grant_category.includes(category)
96+ : post.taxonomies.grant_category.includes(category)
97− : true;
97+ : true;
98
98
99 const notCanceled = !post.extra.canceled;
99 const notCanceled = !post.extra.canceled;
100 const hasType = type ? post.taxonomies.grant_type.includes(type) : true;
100 const hasType = type ? post.taxonomies.grant_type.includes(type) : true;
@@ -107,6 +107,9 @@ export default function Grants({ posts, categories, types, search }) {
107 Bounty: postsByStatus.filter((post) =>
107 Bounty: postsByStatus.filter((post) =>
108 post.taxonomies.grant_type.includes("Bounty")
108 post.taxonomies.grant_type.includes("Bounty")
109 ).length,
109 ).length,
110+ RFP: postsByStatus.filter((post) =>
111+ post.taxonomies.grant_type.includes("RFP")
112+ ).length,
110 Apprenticeship: postsByStatus.filter((post) =>
113 Apprenticeship: postsByStatus.filter((post) =>
111 post.taxonomies.grant_type.includes("Apprenticeship")
114 post.taxonomies.grant_type.includes("Apprenticeship")
112 ).length,
115 ).length,
@@ -122,7 +125,7 @@ export default function Grants({ posts, categories, types, search }) {
122 {Meta({
125 {Meta({
123 title: "Grants",
126 title: "Grants",
124 description:
127 description:
125− "Contribute to the Urbit project while earning address space.",
128+ "Contribute to the Urbit project while earning address space.",
126 })}
129 })}
127 </Head>
130 </Head>
128 <IntraNav ourSite="https://urbit.org" search={search} />
131 <IntraNav ourSite="https://urbit.org" search={search} />
@@ -157,11 +160,18 @@ export default function Grants({ posts, categories, types, search }) {
157 fund all kinds projects, not strictly technical ones, so don't
160 fund all kinds projects, not strictly technical ones, so don't
158 hesitate to pitch your idea!
161 hesitate to pitch your idea!
159 </p>
162 </p>
163+ <div className="flex flex-wrap mb-8">
160− <Link href="/grants/proposals" passHref>
164+ <Link href="/grants/proposals" passHref>
161− <a className="button-sm bg-blue-400 text-white mb-8 max-w-fit">
165+ <a className="button-sm bg-blue-400 text-white mb-8 max-w-fit mr-2">
162− Submit a Proposal
166+ Submit a Proposal
163− </a>
167+ </a>
168+ </Link>
169+ <Link href="/grants?program=rfps&type=RFP#view-grants" passHref>
170+ <a className="button-sm bg-purple text-white mb-8 max-w-fit">
171+ Get Ideas
172+ </a>
164− </Link>
173+ </Link>
174+ </div>
165
175
166 {/* Apprenticeships */}
176 {/* Apprenticeships */}
167 <h3>Apprenticeships</h3>
177 <h3>Apprenticeships</h3>
@@ -197,15 +207,7 @@ export default function Grants({ posts, categories, types, search }) {
197 </a>
207 </a>
198 </Link>
208 </Link>
199 </div>
209 </div>
200−
201−
202− {/* Approval */}
203− <h3>Approval</h3>
204− <p className="mb-4">
205− Grant applications and proposals are approved on a bi-weekly basis. The next deadlines are <b>March 14th & 28th</b>.
206− </p>
207− <p>If you any questions regarding grants, feel free to email grants@urbit.org.</p>
208− </div>
210+ </div>
209 </div>
211 </div>
210 </div>
212 </div>
211 </Section>
213 </Section>
@@ -235,6 +237,12 @@ export default function Grants({ posts, categories, types, search }) {
235 by those that post them.
237 by those that post them.
236 </p>
238 </p>
237 </li>
239 </li>
240+ <li className="mb-2">
241+ <p>
242+ <Link href="/grants/rfps">RFPs</Link>{" "}
243+ are suitable for people experienced with hoon development but who need some ideas on what to work on.
244+ </p>
245+ </li>
238 <li className="mb-2">
246 <li className="mb-2">
239 <p>
247 <p>
240 <Link href="/grants/apprenticeships">Apprenticeships</Link>{" "}
248 <Link href="/grants/apprenticeships">Apprenticeships</Link>{" "}
@@ -248,9 +256,16 @@ export default function Grants({ posts, categories, types, search }) {
248 <div className="md:basis-1/2">
256 <div className="md:basis-1/2">
249 <p className="mb-8">
257 <p className="mb-8">
250 Whether you're gearing up to submit your proposal or just
258 Whether you're gearing up to submit your proposal or just
251− thinking about it, the best way to get started is to join the <Link href="/groups/~halbex-palheb/uf-public">Urbit Foundation</Link> group.
259+ thinking about it, the best way to get started is to join the <Link href="/groups/~halbex-palheb/uf-public">Urbit Foundation</Link> group, ask questions there or come find our grants office hours in the group.
260+ </p>
261+ <p className="mb-8">
262+ Feel free to contact <a href="/ids/~poldec-tonteg">~poldec-tonteg</a> with general questions or <a href="/ids/~marfun-pacpet">~marfun-pacpet</a> regarding administrative matters.
263+ </p>
264+ <p className="mb-8">
265+ <h4>Approval</h4>
266+ </p>
267+ <p className="mb-8">Grant applications and proposals are approved on a bi-weekly basis. The next deadlines are <b>March 14th & 28th</b>.
252 </p>
268 </p>
253− <p className="mb-8">Feel free to contact <a href="/ids/~poldec-tonteg">~poldec-tonteg</a> with general questions or <a href="/ids/~marfun-pacpet">~marfun-pacpet</a> regarding administrative matters.</p>
254 </div>
269 </div>
255 </div>
270 </div>
256 </Section>
271 </Section>
@@ -272,17 +287,20 @@ export default function Grants({ posts, categories, types, search }) {
272 {types.map((each) => {
287 {types.map((each) => {
273 let activeBg;
288 let activeBg;
274 switch (each) {
289 switch (each) {
275− case "Proposal":
290+ case "Proposal":
276− activeBg = "bg-blue-400 text-white";
291+ activeBg = "bg-blue-400 text-white";
277− break;
292+ break;
278− case "Bounty":
293+ case "Bounty":
279− activeBg = "bg-yellow-300 text-white";
294+ activeBg = "bg-yellow-300 text-white";
280− break;
295+ break;
296+ case "RFP":
297+ activeBg = "bg-purple-400 text-white";
298+ break;
281− case "Apprenticeship":
299+ case "Apprenticeship":
282− activeBg = "bg-green-400 text-white";
300+ activeBg = "bg-green-400 text-white";
283− break;
301+ break;
284− default:
302+ default:
285− activeBg = "bg-black";
303+ activeBg = "bg-black";
286 }
304 }
287 return (
305 return (
288 <a
306 <a
@@ -302,8 +320,8 @@ export default function Grants({ posts, categories, types, search }) {
302 <div className="flex flex-wrap mb-12">
320 <div className="flex flex-wrap mb-12">
303 {categories.map((each) => {
321 {categories.map((each) => {
304 const active = isOrIsIn(each, category)
322 const active = isOrIsIn(each, category)
305− ? "bg-green-400 text-white"
323+ ? "bg-green-400 text-white"
306− : "bg-wall-100 text-wall-500";
324+ : "bg-wall-100 text-wall-500";
307 const categoryQuery = pushOrDropQuery("category", category, each);
325 const categoryQuery = pushOrDropQuery("category", category, each);
308 return (
326 return (
309 <button
327 <button
@@ -319,20 +337,20 @@ export default function Grants({ posts, categories, types, search }) {
319 {["open", "complete", "wip"].map((each) => {
337 {["open", "complete", "wip"].map((each) => {
320 let title;
338 let title;
321 switch (each) {
339 switch (each) {
322− case "open":
340+ case "open":
323− title = isOrIsIn(each, status)
341+ title = isOrIsIn(each, status)
324− ? "Exclude Open"
342+ ? "Exclude Open"
325− : "Include Open";
343+ : "Include Open";
326− break;
344+ break;
327− case "complete":
345+ case "complete":
328− title = isOrIsIn(each, status)
346+ title = isOrIsIn(each, status)
329− ? "Exclude Completed"
347+ ? "Exclude Completed"
330− : "Include Completed";
348+ : "Include Completed";
331− break;
349+ break;
332− case "wip":
350+ case "wip":
333− title = isOrIsIn(each, status)
351+ title = isOrIsIn(each, status)
334− ? "Exclude In Progress"
352+ ? "Exclude In Progress"
335− : "Include In Progress";
353+ : "Include In Progress";
336 }
354 }
337 const statusQuery = pushOrDropQuery("status", status, each);
355 const statusQuery = pushOrDropQuery("status", status, each);
338 return (
356 return (
@@ -371,7 +389,7 @@ export async function getStaticProps() {
371 "date"
389 "date"
372 )
390 )
373 }).flat()
391 }).flat()
374− .sort((a, b) => new Date(b.date) - new Date(a.date));
392+ .sort((a, b) => new Date(b.date) - new Date(a.date));
375
393
376 return {
394 return {
377 props: {
395 props: {
pages/grants/[slug].js+67−50
@@ -35,39 +35,39 @@ export default function Grant({ post, markdown, match, search }) {
35 const assignee = post?.extra?.assignee;
35 const assignee = post?.extra?.assignee;
36 const mentor = post.extra.mentor || post.extra.champion;
36 const mentor = post.extra.mentor || post.extra.champion;
37 const assigneeLink =
37 const assigneeLink =
38− assignee &&
38+ assignee &&
39− assignee.map((each, index) => {
39+ assignee.map((each, index) => {
40− return ob.isValidPatp(each) ? (
40+ return ob.isValidPatp(each) ? (
41− <>
41+ <>
42− {index ? ", " : ""}
42+ {index ? ", " : ""}
43− <Link href={`/ids/${each}`} passHref>
43+ <Link href={`/ids/${each}`} passHref>
44− <a className="text-green-400">{each}</a>
44+ <a className="text-green-400">{each}</a>
45− </Link>
45+ </Link>
46− </>
46+ </>
47− ) : (
47+ ) : (
48− <>
48+ <>
49− {index ? ", " : ""}
49+ {index ? ", " : ""}
50− {each}
50+ {each}
51− </>
51+ </>
52− );
52+ );
53− });
53+ });
54 const mentorLink =
54 const mentorLink =
55− mentor &&
55+ mentor &&
56− mentor.map((each, index) => {
56+ mentor.map((each, index) => {
57− return ob.isValidPatp(each) ? (
57+ return ob.isValidPatp(each) ? (
58− <>
58+ <>
59− {index ? ", " : ""}
59+ {index ? ", " : ""}
60− <Link href={`/ids/${each}`} passHref>
60+ <Link href={`/ids/${each}`} passHref>
61− <a className="text-green-400">{each}</a>
61+ <a className="text-green-400">{each}</a>
62− </Link>
62+ </Link>
63− </>
63+ </>
64− ) : (
64+ ) : (
65− <>
65+ <>
66− {index ? ", " : ""}
66+ {index ? ", " : ""}
67− {each}
67+ {each}
68− </>
68+ </>
69− );
69+ );
70− });
70+ });
71
71
72 let status = "";
72 let status = "";
73 if (isOpen) {
73 if (isOpen) {
@@ -85,21 +85,21 @@ export default function Grant({ post, markdown, match, search }) {
85 "bg-green-400 text-white": status === "Completed" || status === "Open",
85 "bg-green-400 text-white": status === "Completed" || status === "Open",
86 "bg-wall-300": status === "In Progress",
86 "bg-wall-300": status === "In Progress",
87 })}>
87 })}>
88− {status}
88+ {status}
89− </div>;
89+ </div>;
90
90
91 let deliverable = null;
91 let deliverable = null;
92 if (post.extra.deliverable) {
92 if (post.extra.deliverable) {
93 if (match) {
93 if (match) {
94 deliverable = <ResourceCard
94 deliverable = <ResourceCard
95− title={match.data.title}
95+ title={match.data.title}
96− image={match.data.image}
96+ image={match.data.image}
97− color={match.data.bgColor || "#000000"}
97+ color={match.data.bgColor || "#000000"}
98− description={match.data?.description || ""}
98+ description={match.data?.description || ""}
99− type="application"
99+ type="application"
100− shortcode={post.extra.deliverable}
100+ shortcode={post.extra.deliverable}
101− full
101+ full
102− />
102+/>
103 }
103 }
104 else if (post.extra.deliverable.startsWith("~")) {
104 else if (post.extra.deliverable.startsWith("~")) {
105 deliverable = <JoinGroup emphasize groupName={post.extra.deliverable} />
105 deliverable = <JoinGroup emphasize groupName={post.extra.deliverable} />
@@ -111,6 +111,13 @@ export default function Grant({ post, markdown, match, search }) {
111 const metaPost = Object.assign({}, post);
111 const metaPost = Object.assign({}, post);
112 metaPost.title = `${post.title} - Grant`;
112 metaPost.title = `${post.title} - Grant`;
113
113
114+ const isRFP = post.taxonomies.grant_type.filter((category) => {
115+ return category === "RFP";
116+ }).length > 0;
117+
118+ const hasChampion = post.extra.champion && post.extra.champion[0] !== "";
119+ const hasAssignee = post.extra.assignee && post.extra.assignee[0] !== "";
120+
114 return (
121 return (
115 <Container>
122 <Container>
116 <Head>
123 <Head>
@@ -126,6 +133,7 @@ export default function Grant({ post, markdown, match, search }) {
126 {statusBadge}
133 {statusBadge}
127 {post.taxonomies.grant_type.map((category) => {
134 {post.taxonomies.grant_type.map((category) => {
128 const className = classnames({
135 const className = classnames({
136+ "bg-purple-400 text-white": category === "RFP",
129 "bg-blue-400 text-white": category === "Proposal",
137 "bg-blue-400 text-white": category === "Proposal",
130 "bg-green-400 text-white": category === "Apprenticeship",
138 "bg-green-400 text-white": category === "Apprenticeship",
131 "bg-yellow-300": category === "Bounty",
139 "bg-yellow-300": category === "Bounty",
@@ -161,13 +169,13 @@ export default function Grant({ post, markdown, match, search }) {
161 content={mentorLink}
169 content={mentorLink}
162 />
170 />
163 ) : null}
171 ) : null}
164− {post.extra.champion ? (
172+ {hasChampion ? (
165 <MetadataBlock
173 <MetadataBlock
166 title="Champion"
174 title="Champion"
167 content={mentorLink}
175 content={mentorLink}
168 />
176 />
169 ) : null}
177 ) : null}
170− {post.extra.assignee ? (
178+ {hasAssignee ? (
171 <MetadataBlock
179 <MetadataBlock
172 title="Grantee(s)"
180 title="Grantee(s)"
173 content={assigneeLink}
181 content={assigneeLink}
@@ -175,17 +183,26 @@ export default function Grant({ post, markdown, match, search }) {
175 ) : null}
183 ) : null}
176 </div>
184 </div>
177 {post?.extra?.link?.startsWith("~") && <div className="flex flex-col my-4 space-y-2">
185 {post?.extra?.link?.startsWith("~") && <div className="flex flex-col my-4 space-y-2">
178− <p className="text-wall-400 font-semibold">Group</p>
186+ <p className="text-wall-400 font-semibold">Group</p>
179− <JoinGroup emphasize groupName={post.extra.link} />
187+ <JoinGroup emphasize groupName={post.extra.link} />
180− </div>}
188+ </div>}
181 {deliverable && <div className="flex flex-col my-4 space-y-2">
189 {deliverable && <div className="flex flex-col my-4 space-y-2">
182− <p className="text-wall-400 font-semibold">Deliverable</p>
190+ <p className="text-wall-400 font-semibold">Deliverable</p>
183− {deliverable}
191+ {deliverable}
184− </div>}
192+ </div>}
185 </Section>
193 </Section>
186 <Section narrow className="markdown">
194 <Section narrow className="markdown">
187 <Markdown.render content={JSON.parse(markdown)} />
195 <Markdown.render content={JSON.parse(markdown)} />
188 </Section>
196 </Section>
197+ {isRFP && (
198+ <a
199+ className="bg-green-400 text-white button-lg"
200+ href="/grants/rfps"
201+ target="_blank"
202+ >
203+ Learn More
204+ </a>
205+ )}
189 {canApply && (
206 {canApply && (
190 <a
207 <a
191 className="bg-green-400 text-white button-lg"
208 className="bg-green-400 text-white button-lg"
pages/grants/apprenticeships.js+1−1
@@ -8,7 +8,7 @@ export default function Post({ post, markdown, search }) {
8 post={post}
8 post={post}
9 markdown={markdown}
9 markdown={markdown}
10 search={search}
10 search={search}
11− actionText="View Apprenticeships"
11+ actionText="View Requests"
12 actionLink="/grants?program=apprenticeship&completed=true#view-grants"
12 actionLink="/grants?program=apprenticeship&completed=true#view-grants"
13 />
13 />
14 );
14 );
pages/grants/rfps.jsnew file+29
@@ -0,0 +1,29 @@
1+import GrantProgramOverview from "../../components/GrantProgramOverview";
2+import { Markdown, getPostBySlug } from "@urbit/foundation-design-system";
3+
4+export default function Post({ post, markdown, search }) {
5+ return (
6+ <GrantProgramOverview
7+ program="rfps"
8+ post={post}
9+ markdown={markdown}
10+ search={search}
11+ actionText="View RFPs"
12+ actionLink="/grants?program=rfps#view-grants"
13+ />
14+ );
15+}
16+
17+export async function getStaticProps() {
18+ const post = getPostBySlug(
19+ "/rfps",
20+ ["title", "date", "slug", "content"],
21+ "/"
22+ );
23+
24+ const markdown = JSON.stringify(Markdown.parse({ post }));
25+
26+ return {
27+ props: { post, markdown },
28+ };
29+}