diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..101afd10 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..24473dee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 31d83303..9b95fb52 100644 --- a/.gitignore +++ b/.gitignore @@ -68,17 +68,12 @@ typings/ # Yarn Integrity file .yarn-integrity -# dotenv environment variables file -.env -.env.test -.env.qa -.env.prod - # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next +out # Nuxt.js build / generate output .nuxt @@ -110,3 +105,30 @@ dist *.iws *.iml *.ipr + +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env*.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +package-lock.json +contracts/coverage.json +contracts/build + +# Local Netlify folder +.netlify \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..17df4593 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn workspace @bounty-board/react-app lint-staged diff --git a/README.md b/README.md index cb39a8e7..f5af647b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,88 @@ # DAO Bounty Board +[![Netlify Status](https://api.netlify.com/api/v1/badges/8e12fc34-94a3-4c7a-8323-fc2b7e1d4d14/deploy-status)](https://app.netlify.com/sites/bounty-board-29081e/deploys) + Project Page: https://www.notion.so/bankless/Bounty-Board-318dc164cc5640cca17e0fb5f484fd90 - ### Specifications: - [Specs page](https://docs.google.com/document/d/1VJXin9Uoqt54JjfQEtyM11XESb2l4C1SSAzgc0kvxIs/edit?usp=sharing) - [Figma](https://www.figma.com/file/venmq7OWr8iRsjR9ecttvc/Untitled?node-id=1%3A7) - [Whimsical](https://whimsical.com/bounty-board-design-bankless-2cPEEZinYKJ2zE2Zvq7iAZ) - [Heroku](https://bounty-board.herokuapp.com/) +- [Possible Queries](https://www.notion.so/Bounty-Board-Queries-33d03a4330454e67b8194aa86274ec34) +- [Data Fields](https://docs.google.com/document/d/10jgHxEpkPlArGlsQH1g22utFrAFh2lK-fbXjbq8KkuU/edit) +- [Personas](https://www.notion.so/Bounty-Board-Personas-8e8f2789775a445c82d13c2a9c545185) +- [Bot flowchart](https://media.discordapp.net/attachments/852736763205910538/857786834682511370/image0.jpg?width=978&height=683) + +# Project Overview + +## Problem + +Currently, Bankless DAO bounties are not in a centralized location causing confusion and makes it challenging for members, new and old, to contribute. Also Level 0's do not have intuitive ways to get involved and earn $BANK other than buying in the secondary markets. + +## Solution + +For the DAO to grow, we need a way to attract, retain and coordinate talent. The bounty board, accessible to members and non-members will connect the DAO to a continually expanding talent pool. + +In addition, we need a way to codify meaningful units of work. Given the diversity of jobs to be done in a DAO, the bounty board allows bounty _creators_ to define and specify the scope of tasks along with expected deliverables. + +Finally, we need a way to formalize the flow of capital, beyond an informal, organic tipping culture that exists to formally recognize contributors for their knowledge, skills and abilities. + +The bounty board will be a key mechanism for coordinating talent, tasks and capital. + +## Minimal Viable Product 1.0 + +### Bounty Card Definition + +For the MVP, we are focusing on the bare requirements for a Bounty Card to be created by a user via DEGEN and/or Frontend UI, with the following key fields: + +- **Title**: Bounty Titles should be like headlines +- **Description**: Provides space to flesh out the scope, deliverables and timeline for the bounty. +- **Criteria**: When is a task considered "done" or "complete"? +- **Reward**: Bounty creator indicates `amount` (i.e., 10000) and `currency` (i.e., $BANK) to be paid for completing the work. +- **HashId**: Auto-generated ID for each bounty. +- **CreatedBy / RequestedBy**: Bot automatically enters bounty creator's `discordHandle`. +- **ClaimedBy**: Bot automatially notes `discordHandle` claiming the bounty. +- **SubmittedBy**: Bot automatically notes `discordHandle` submitting the bounty. + +#### User Experience Flow: Bot in Discord + +The User Experience flow with accompanying bounty status is as follows: + +1. **Draft**: Bounty creator creates new bounty with `/bounty create new`; status is now "Draft" and _not_ shown in Discord. Bounty creator must publish the bounty before it is available to the public. +2. **Publish/Open**: Bounty creator clicks thumbs up emoji đź‘Ť or `/bounty create publish` in Discord, bounty published to #🧀-bounty-board channel and website (url provided); status is now _Open_ on website. +3. **Claim/In Progress**: Within #🧀-bounty-board Bounty _claimer_ click black flag 🏴 or `/bounty claim` to 'start', card changes color in Discord, Bounty creator receives message that bounty has been claimed; Bounty card on website now has _Claimed By_; card status is now "In Progress". +4. **Submit/In Review**: Bounty claimer hits red mail box emoji đź“® in Discord, receives auto-generated message from Bot indicating "bounty in review". The creator of the bounty is notified that the bounty is ready for review. They should reach out to the claimee. Alternatively, user can submit directly through a new bot command `/bounty submit`, entering `HashId`; card status is now "In Review". +5. **Complete/Completed**: Bounty claimer can signal completion âś… on the post in the #🧀-bounty-board channel or directly through a new bot command `/bounty complete true`; card status is now "Completed". + +#### Bot Commands + +1. Within The Bankless Bot Garage, head to #spam-tastic +2. Enter `/` and see a list of Bots pop up, choose `SERENDIPITY MK-I` + +The following commands are available for Serendipity MK-I: + +/bounty create new +/bounty create publish +/bounty claim +/bounty complete +/bounty list +/bounty delete +/bounty submit + +Refer to the [Bounty Board Commands and Workflow](https://bankless.notion.site/The-Bounty-Board-Commands-and-Workflow-7f15bbc3f2c744afab1cb5f90daac4a2) Notion Page for in-depth details. + +#### User Experience Flow: Frontend + +**Note**: Currently, the frontend mirrors the interaction with the Bot in discord and displays changes in card status. Both Discord/Bot actions and Frontend Statuses are displayed below: + +1. **Discord/Bot: Draft**: Bounty creator creates new bounty with `/bounty create new`; status is now "Draft" and _not_ shown in Discord. Bounty creator must publish the bounty before it is available on the frontend. +2. **Frontend Status: Open**: Bounty creator clicks thumbs up emoji đź‘Ť or `/bounty create publish` in Discord, bounty published to #🧀-bounty-board channel and website (url provided); status is now _Open_ on the frontend. +3. **Discord/Bot: Claim**: Now that a bounty card is _Open_, we can click "Claim It". +4. **Frontend Status: In Progress**: Within #🧀-bounty-board Bounty click black flag 🏴 or `/bounty claim` to 'claim'. A link back to the frontend shows card status as "In Progress" and "Claimed By" claimer's discord handle. +5. **Discord/Bot: Submit**: Bounty claimer hits red mail box emoji đź“® in Discord, receives auto-generated message from Bot indicating "bounty in review". +6. **Frontend Status: In Review**: Card status on the frontend is "In Review". +7. **Discord/Bot: Complete**: Bounty claimer can signal completion âś… on the post in the #🧀-bounty-board channel. Bounty creator receives message to tip bounty completer. +8. **Frontend Status: Completed**: Work is done. diff --git a/mongo/bounties/bboard_final.json b/mongo/bounties/bboard_final.json new file mode 100644 index 00000000..22b84280 --- /dev/null +++ b/mongo/bounties/bboard_final.json @@ -0,0 +1,87 @@ +[ + { + "_id": "60f6585b453e70eed340e8e3", + "season": 1, + "title": "Make new website landing page", + "description": "Description for new website landing page with specs", + "criteria": "A completed bounty has the following deliverables...", + "reward": { + "currency": "BANK", + "amount": 10000, + "precision": 2 + }, + "applicableGuilds": [ + { + "name": "Developers", + "id": "60f6585b453e70eed340e8e3" + }, + { + "name": "Marketing Guild", + "id": "60f6585b453e70eed340e8e3" + } + ], + "createdBy": { + "isDaoMember": true, + "guildName": "Analytics Guild", + "discordHandle": "paul#8888", + "discordId": "324439902343239764" + }, + "createdAt": "2021-07-20T06:40:56.112Z", + "dueAt": "2021-07-20T06:42:28.853Z", + "activatedAt": "2021-07-20T06:44:33.858Z", + "claimedBy": [ + { + "isDaoMember": false, + "guildName": null, + "discordHandle": "rick#5555", + "discordId": "324423432343239764" + } + ], + "claimedAt": "2021-07-20T07:00:31.166Z", + "completedAt": "2021-07-20T07:00:31.166Z", + "submissionUrl": "https://example.org/rels/v1/bounty_submission", + "submissionNotes": "it took me a long time to work on this bounty", + "status": "Draft", + "statusHistory": [ + { + "status": "Open", + "modifiedAt": "2021-07-20T07:00:31.166Z" + }, + { + "status": "Draft", + "modifiedAt": "2021-07-20T07:00:31.166Z" + }, + { + "status": "In-Progress", + "modifiedAt": "2021-07-20T07:00:31.166Z" + }, + { + "status": "In-Review", + "modifiedAt": "2021-07-20T07:00:31.166Z" + }, + { + "status": "Completed", + "modifiedAt": "2021-07-20T07:00:31.166Z" + }, + { + "status": "Deleted", + "modifiedAt": "2021-07-20T07:00:31.166Z" + } + ], + "hash": "96efcf14fe28a4f17a07a5441b00285b12cbb208bf3c1bdbb030bb95d3e31a8b", + "skillsRequired": [ + "writing", + "design", + "frontend software development", + "backend software development", + "strategic planning", + "data analysis", + "grant writing", + "proposal development", + "team building", + "consensus building", + "marketing", + "sales" + ] + } +] \ No newline at end of file diff --git a/mongo/bounties/bboard_v1.json b/mongo/bounties/bboard_v1.json new file mode 100644 index 00000000..759b5eee --- /dev/null +++ b/mongo/bounties/bboard_v1.json @@ -0,0 +1,76 @@ +[ + { + "//": "Comment: bountyHash message -> bankless dao bounty board" + }, + { + "season" : 2, + "bountyTitle": "A new About Us webpage", + "bountyDescription": "Description of webpage", + "bountyCriteria": "A completed bounty has the following...", + "bountyReward": { + "currency" : "BANK", + "amount" : 5000 + }, + "applicableGuilds": [ + {"guildName": "Marketing Guild"}, + {"guildName": "Treasury Guild"}, + {"guildName": "Developer's Guild"}, + {"guildName": "Analytics Guild"}, + {"guildName": "Writer's Guild"} + ], + "bountyCreatedBy": { + "isDaoMember" : true, + "guildName" : "Analytics Guild", + "discordHandle" : "@paulapivat#8888" + }, + "bountyCreatedAt": "2021-07-10T13:49:51.141Z", + "bountyDueAt" : "2021-09-11T13:49:51.141", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12T13:49:51.141Z", + "bountyClaimedBy": [ + {"guildName": "Marketing Guild", "discordHandle" : "@random#1234", "publicAddress" : "0x2d94aa3e47d9d5024503ca8491fce9a2fb4da198"} + ], + "bountyClaimedAt": "2021-07-11T13:49:51.141Z", + "bountySubmittedBy": "Developer's Guild", + "bountySubmittedAt": "2021-08-11T13:49:51.141Z", + "bountySubmissionLink": "https://example.org/rels/v1/bounty_submission", + "bountyStatus": [ + { + "status" : "Open", + "openAt" : "2021-07-10T13:49:51.141Z" + }, + { + "status" : "Draft", + "draftAt" : "2021-07-10T13:49:51.141Z" + }, + { + "status" : "In-Progress", + "progressAt" : "2021-07-10T13:49:51.141Z" + }, + { + "status" : "In-Review", + "reviewAt" : "2021-07-10T13:49:51.141Z" + }, + { + "status" : "Completed", + "completedAt" : "2021-07-10T13:49:51.141Z" + }, + { + "status" : "Deleted", + "closedAt" : "2021-07-10T13:49:51.141Z" + } + ], + "bountyHash" : "96efcf14fe28a4f17a07a5441b00285b12cbb208bf3c1bdbb030bb95d3e31a8b", + "skillsRequired" : [ + "writing", + "design", + "software development", + "strategic planning", + "data analysis", + "grant writing", + "proposal development", + "team building", + "marketing" + ] + } +] \ No newline at end of file diff --git a/mongo/bounties/bboard_v2.json b/mongo/bounties/bboard_v2.json new file mode 100644 index 00000000..1dc98132 --- /dev/null +++ b/mongo/bounties/bboard_v2.json @@ -0,0 +1,232 @@ +[ + { + "season": 3, + "bounty": "Thus box music year fund level.", + "bountyDescription": "Visit address me item.", + "bountyCriteria": "Hand draw get listen significant officer.", + "bountyReward": { + "currency": "BTC", + "amount": 47685 + }, + "applicableGuilds": [ + { + "guildName": "Analytics Guild" + }, + { + "guildName": "Treasury Guild" + } + ], + "bountyCreatedBy": { + "isDaoMember": true, + "guildName": "Analytics Guild", + "discordHandle": "@delta#2222" + }, + "bountyCreatedAt": "2021-07-12", + "bountyDueAt": "2021-12-22", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12", + "bountyClaimedBy": { + "guildName": "Treasury Guild", + "discordHandle": "@alice#1234" + }, + "bountyClaimedAt": "2021-07-12", + "bountySubmittedBy": "Writer's Guild", + "bountySubmittedAt": "2021-07-12", + "bountySubmissionLink": "www.example.org", + "bountyStatus": [ + { + "status": "Draft", + "bountyStatusTime": "2022-05-03" + } + ], + "bountyHash": "eac65c7b6f5a37df3dee998a0afcfde7", + "skillsRequired": [ + "marketing", + "strategic planning" + ] + }, + { + "season": 10, + "bounty": "Pull sit their will.", + "bountyDescription": "Impact story talk site everybody particular or.", + "bountyCriteria": "Some move between plant where marriage.", + "bountyReward": { + "currency": "BTC", + "amount": 11239 + }, + "applicableGuilds": [ + { + "guildName": "Marketing Guild" + }, + { + "guildName": "Developer's Guild" + } + ], + "bountyCreatedBy": { + "isDaoMember": false, + "guildName": "Marketing Guild", + "discordHandle": "@delta#2222" + }, + "bountyCreatedAt": "2021-07-12", + "bountyDueAt": "2022-03-27", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12", + "bountyClaimedBy": { + "guildName": "Marketing Guild", + "discordHandle": "@delta#2222" + }, + "bountyClaimedAt": "2021-07-12", + "bountySubmittedBy": "Treasury Guild", + "bountySubmittedAt": "2021-07-12", + "bountySubmissionLink": "www.example.net", + "bountyStatus": [ + { + "status": "Open", + "bountyStatusTime": "2022-02-25" + } + ], + "bountyHash": "8c38d010d86de1feda23f5d5727e78f0", + "skillsRequired": [ + "writing", + "data analysis" + ] + }, + { + "season": 2, + "bounty": "Condition daughter current system thing go reduce.", + "bountyDescription": "Other quite experience high.", + "bountyCriteria": "Space instead senior spend history better population.", + "bountyReward": { + "currency": "BANK", + "amount": 40975 + }, + "applicableGuilds": [ + { + "guildName": "Writer's Guild" + }, + { + "guildName": "Developer's Guild" + } + ], + "bountyCreatedBy": { + "isDaoMember": true, + "guildName": "Developer's Guild", + "discordHandle": "@delta#2222" + }, + "bountyCreatedAt": "2021-07-12", + "bountyDueAt": "2021-10-10", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12", + "bountyClaimedBy": { + "guildName": "Treasury Guild", + "discordHandle": "@delta#2222" + }, + "bountyClaimedAt": "2021-07-12", + "bountySubmittedBy": "Developer's Guild", + "bountySubmittedAt": "2021-07-12", + "bountySubmissionLink": "www.example.com", + "bountyStatus": [ + { + "status": "In-Progress", + "bountyStatusTime": "2021-10-18" + } + ], + "bountyHash": "aaf853be8ca2f1e30b4c83b1d4bfcbde", + "skillsRequired": [ + "design", + "data analysis" + ] + }, + { + "season": 10, + "bounty": "Study without set staff.", + "bountyDescription": "Series former its five off strategy.", + "bountyCriteria": "Term walk arrive nothing name onto.", + "bountyReward": { + "currency": "ETH", + "amount": 35450 + }, + "applicableGuilds": [ + { + "guildName": "Treasury Guild" + }, + { + "guildName": "Developer's Guild" + } + ], + "bountyCreatedBy": { + "isDaoMember": false, + "guildName": "Writer's Guild", + "discordHandle": "@alice#1234" + }, + "bountyCreatedAt": "2021-07-12", + "bountyDueAt": "2022-06-05", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12", + "bountyClaimedBy": { + "guildName": "Developer's Guild", + "discordHandle": "@delta#2222" + }, + "bountyClaimedAt": "2021-07-12", + "bountySubmittedBy": "Developer's Guild", + "bountySubmittedAt": "2021-07-12", + "bountySubmissionLink": "www.example.org", + "bountyStatus": [ + { + "status": "Open", + "bountyStatusTime": "2021-08-08" + } + ], + "bountyHash": "72e04fe31953195ab0911e96cf59364f", + "skillsRequired": [ + "strategic planning", + "writing" + ] + }, + { + "season": 2, + "bounty": "Instead range most outside government without above.", + "bountyDescription": "Say tree effect wait avoid.", + "bountyCriteria": "Lot year meet analysis media cold local.", + "bountyReward": { + "currency": "ETH", + "amount": 13166 + }, + "applicableGuilds": [ + { + "guildName": "Marketing Guild" + }, + { + "guildName": "Treasury Guild" + } + ], + "bountyCreatedBy": { + "isDaoMember": true, + "guildName": "Treasury Guild", + "discordHandle": "@delta#2222" + }, + "bountyCreatedAt": "2021-07-12", + "bountyDueAt": "2022-04-16", + "bountyImage": "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "bountyActivatedAt": "2021-07-12", + "bountyClaimedBy": { + "guildName": "Marketing Guild", + "discordHandle": "@alice#1234" + }, + "bountyClaimedAt": "2021-07-12", + "bountySubmittedBy": "Developer's Guild", + "bountySubmittedAt": "2021-07-12", + "bountySubmissionLink": "www.example.net", + "bountyStatus": [ + { + "status": "Draft", + "bountyStatusTime": "2021-11-09" + } + ], + "bountyHash": "b1df4577e03129eb96de415eace2bd2e", + "skillsRequired": [ + "marketing", + "team building" + ] + } +] \ No newline at end of file diff --git a/mongo/bounties/bboard_v3.json b/mongo/bounties/bboard_v3.json new file mode 100644 index 00000000..55d722d1 --- /dev/null +++ b/mongo/bounties/bboard_v3.json @@ -0,0 +1,102 @@ +[ + { + "//": "Comment: bountyHash message -> bankless dao bounty board" + }, + { + "_id" : ObjectId("60f6558d453e70eed340e8e0"), + "isDaoMember" : true, + "guildName" : "Analytics Guild", + "discordHandle" : "@paul#8888" + }, + { + "_id" : ObjectId("60f6558d453e70eed340e8e1"), + "isDaoMember" : false, + "guildName" : null, + "discordHandle" : "@rick#5555" + }, + { + "_id" : ObjectId("60f6558d453e70eed340e8e2"), + "isDaoMember" : true, + "guildName" : "Developers Guild", + "discordHandle" : "@james#7777" + }, + { + "_id" : ObjectId("60f6585b453e70eed340e8e3"), + "season" : 1, + "Title" : "Make new website landing page", + "Description" : "Description for new website landing page with specs", + "Criteria" : "A completed bounty has the following deliverables...", + "Reward" : { + "currency" : "BANK", + "amount" : 10000 + }, + "applicableGuilds" : [ + { + "guildName" : "Developers Guild" + }, + { + "guildName" : "Marketing Guild" + } + ], + "CreatedBy" : { + "user" : ObjectId("60f6558d453e70eed340e8e0") + }, + "CreatedAt" : ISODate("2021-07-20T06:40:56.112Z"), + "DueAt" : ISODate("2021-07-20T06:42:28.853Z"), + "Image" : "https://pbs.twimg.com/profile_images/1389400052448247816/qsOU0pih_400x400.jpg", + "ActivatedAt" : ISODate("2021-07-20T06:44:33.858Z"), + "ClaimedBy" : [ + { + "user" : ObjectId("60f6558d453e70eed340e8e1") + }, + { + "user" : ObjectId("60f6558d453e70eed340e8e2") + } + ], + "ClaimedAt" : ISODate("2021-07-20T07:00:31.166Z"), + "SubmittedBy" : "Developer's Guild", + "SubmittedAt" : ISODate("2021-07-20T07:01:34.700Z"), + "SubmissionLink" : "https://example.org/rels/v1/bounty_submission", + "Status" : [ + { + "status" : "Open", + "openAt" : ISODate("2021-07-20T07:00:31.166Z") + }, + { + "status" : "Draft", + "draftAt" : ISODate("2021-07-20T07:00:31.166Z") + }, + { + "status" : "In-Progress", + "progressAt" : ISODate("2021-07-20T07:00:31.166Z") + }, + { + "status" : "In-Review", + "reviewAt" : ISODate("2021-07-20T07:00:31.166Z") + }, + { + "status" : "Completed", + "completedAt" : ISODate("2021-07-20T07:00:31.166Z") + }, + { + "status" : "Deleted", + "deletedAt" : ISODate("2021-07-20T07:00:31.166Z") + } + ], + "Hash" : "96efcf14fe28a4f17a07a5441b00285b12cbb208bf3c1bdbb030bb95d3e31a8b", + "skillsRequired" : [ + "writing", + "design", + "frontend software development", + "backend software development", + "strategic planning", + "data analysis", + "grant writing", + "proposal development", + "team building", + "consensus building", + "marketing", + "sales" + ] + } +] \ No newline at end of file diff --git a/mongo/bounties/validation.js b/mongo/bounties/validation.js new file mode 100644 index 00000000..1f5f24f4 --- /dev/null +++ b/mongo/bounties/validation.js @@ -0,0 +1,61 @@ +db.createCollection("bountyCard", { + validator: { + $jsonSchema: { + bsonType: "object", + required: [ + "season", + "Title", + "Description", + "Criteria", + "Reward", + "CreatedBy", + "CreatedAt", + "DueAt", + ], + properties: { + season: { + bsonType: "double", + description: "must be a double and is required", + }, + Title: { + bsonType: "string", + description: "must be a string and is required", + }, + Description: { + bsonType: "string", + description: "must be an string and is required", + }, + Criteria: { + bsonType: "string", + description: "must be an string and is required", + }, + Reward: { + bsonType: "object", + description: "must be an object and is required", + properties: { + currency: { + bsonType: "string", + description: "must be a string and is required", + }, + amount: { + bsonType: "double", + description: "must be a double and is required", + }, + }, + }, + CreatedBy: { + bsonType: "objectId", + description: "must be an objectId and is required", + }, + CreatedAt: { + bsonType: "date", + description: "must be a date and is required", + }, + DueAt: { + bsonType: "date", + description: "must be a date and is required", + }, + }, + }, + }, +}); diff --git a/mongo/bounties/validation_final.js b/mongo/bounties/validation_final.js new file mode 100644 index 00000000..fbfc0f1e --- /dev/null +++ b/mongo/bounties/validation_final.js @@ -0,0 +1,91 @@ +db.createCollection("bounties", { + validator: { + $jsonSchema: { + bsonType: "object", + required: [ + "season", + "title", + "description", + "criteria", + "reward", + "createdBy", + "createdAt", + "dueAt", + 'status', + 'statusHistory' + ], + properties: { + season: { + bsonType: "int", + description: "the current season of the DAO, nonzero integer, /^[0,9]+$/", + }, + title: { + bsonType: "string", + description: "a short title about the bounty, /^[\\w\\s.!@#$%&,?']{1,50}$/", + }, + description: { + bsonType: "string", + description: "a short description of the bounty, /^[\\w\\s.!@#$%&,?']{1,250}$/", + }, + criteria: { + bsonType: "string", + description: "absolutely required work for bounty to be Completed, /^[\\w\\s.!@#$%&,?']{1,250}$/", + }, + status: { + bsonType: "string", + description: "the current status of the bounty", + }, + statusHistory: { + bsonType: "array", + description: "the history of the status of the bounty", + }, + reward: { + bsonType: "object", + description: "how much to be distributed to worker once bounty is Completed", + required: [ + "currency", + "amount", + "scale", + ], + properties: { + currency: { + bsonType: "string", + description: "the currency denomination i.e ETH", + }, + amount: { + bsonType: "int", + description: "the amount to be rewarded i.e 1000", + }, + scale: { + bsonType: 'int', + description: 'the decimal value for the given amount' + }, + }, + }, + createdBy: { + bsonType: "object", + description: "user object who created the bounty", + required: ["discordHandle", "discordId"], + properties: { + discordHandle: { + bsonType: "string", + description: "the discord tag i.e hydrabolt#0001", + }, + discordId: { + bsonType: "string", + description: "the discord internal id, i.e 324439906234239764", + } + } + }, + createdAt: { + bsonType: "string", + description: "ISO8601 date string for when the bounty was created", + }, + dueAt: { + bsonType: "string", + description: "ISO8601 date string for when the bounty is due (default to end of season)", + }, + }, + }, + }, +}); diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 00000000..30a05f25 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,27 @@ +# https://docs.netlify.com/configure-builds/file-based-configuration/ +# Settings in the [build] context are global and are applied to all contexts +# unless otherwise overridden by more specific contexts. +[build] + command = "yarn build:qa" + + # Directory that contains the deploy-ready HTML files and assets generated by + # the build. This is relative to the base directory if one has been set, or the + # root directory if a base has not been set. This sample publishes the + # directory located at the absolute path "root/project/build-output" + publish = "packages/react-app/dist" + +[build.environment] + NODE_VERSION = "14.17.5" + +[dev] + command = "yarn dev" + +[[plugins]] + package = "@netlify/plugin-nextjs" + +# Production context: +# All deploys from the main repository branch +# will inherit these settings. +[context.production] + command = "yarn build:prod" + publish = "packages/react-app/dist" diff --git a/package.json b/package.json index 06bf51f1..29bac891 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,40 @@ { - "name": "bounty-board", - "version": "0.1.0", - "description": "This web app displays the bounties that are available for DAO members to claim.", - "main": "app.js", - "repository": "https://github.com/BanklessDAO/bounty-board", - "author": "Bankless DAO", - "license": "MIT", + "name": "@bounty-board/monorepo", + "version": "1.0.0", "private": true, - "bugs": { - "url": "https://github.com/BanklessDAO/bounty-board/issues" - }, - "homepage": "https://github.com/BanklessDAO/bounty-board", "scripts": { - "test": "mocha -r dotenv/config src/test/**/*.test.js", - "qa": "node -r dotenv/config src/app/app.js dotenv_config_path=.env.qa", - "start": "node -r dotenv/config src/app/app.js" + "start": "yarn workspace @bounty-board/react-app start", + "dev": "yarn workspace @bounty-board/react-app dev", + "build": "yarn workspace @bounty-board/react-app build", + "build:qa": "yarn workspace @bounty-board/react-app build:qa", + "build:prod": "yarn workspace @bounty-board/react-app build:prod", + "lint": "yarn workspace @bounty-board/react-app lint", + "test": "yarn workspace @bounty-board/react-app test", + "react-app:lint-staged": "yarn workspace @bounty-board/react-app lint-staged", + "react-app:type-check": "yarn workspace @bounty-board/react-app type-check", + "react-app:format": "yarn workspace @bounty-board/react-app format", + "react-app:test-all": "yarn workspace @bounty-board/react-app test-all", + "truffle:test": "yarn workspace @bounty-board/escrow test", + "truffle:start": "yarn workspace @bounty-board/escrow start", + "truffle:coverage": "yarn workspace @bounty-board/escrow coverage", + "postinstall": "npx husky install" }, - "dependencies": { - "dotenv": "^10.0.0", - "mocha": "^9.0.0", - "mongodb": "^3.6.9", - "nock": "^13.1.0" + "husky": { + "hooks": { + "pre-commit": "react-app:lint-staged" + } }, - "devDependencies": {}, - "engines": { - "node": "14.17.0" + "dependencies": {}, + "workspaces": { + "packages": [ + "packages/*" + ], + "nohoist": [ + "**/truffle", + "**/truffle/**" + ] }, - "engineStrict": true + "devDependencies": { + "husky": "^7.0.1" + } } diff --git a/packages/react-app/.env.prod b/packages/react-app/.env.prod new file mode 100644 index 00000000..a131f7ac --- /dev/null +++ b/packages/react-app/.env.prod @@ -0,0 +1,13 @@ +MONGODB_DB=bountyboard +MONGODB_URI= + +NEXT_PUBLIC_DISCORD_SERVER_ID=834499078434979890 +NEXT_PUBLIC_DISCORD_CHANNEL_BOUNTY_BOARD_ID=850402063741091880 + +# Public Environments +NEXT_PUBLIC_DAO_CURRENT_SEASON=1 +NEXT_PUBLIC_DAO_CURRENT_SEASON_END_DATE=2021-08-31T04:00:00.000Z + +# URLs +NEXT_PUBLIC_DAO_BOUNTY_BOARD_URL=https://bountyboard.bankless.community +DISCORD_BOUNTY_BOARD_WEBHOOK= \ No newline at end of file diff --git a/packages/react-app/.env.qa b/packages/react-app/.env.qa new file mode 100644 index 00000000..1565becd --- /dev/null +++ b/packages/react-app/.env.qa @@ -0,0 +1,13 @@ +MONGODB_DB=bountyboard +MONGODB_URI= + +NEXT_PUBLIC_DISCORD_SERVER_ID=851552281249972254 +NEXT_PUBLIC_DISCORD_CHANNEL_BOUNTY_BOARD_ID=869964294865977425 + +# Public Environments +NEXT_PUBLIC_DAO_CURRENT_SEASON=1 +NEXT_PUBLIC_DAO_CURRENT_SEASON_END_DATE=2021-08-31T04:00:00.000Z + +# URLs +NEXT_PUBLIC_DAO_BOUNTY_BOARD_URL=https://develop--bounty-board-29081e.netlify.app/ +DISCORD_BOUNTY_BOARD_WEBHOOK=https://discord.com/api/webhooks/873289859308073070/cj4sCbAABT3CpOC5f-5XTbJvkDimqEGAye-CFUYGos6V_euKo9jlKXhpJu051ZoNkora \ No newline at end of file diff --git a/packages/react-app/.eslintignore b/packages/react-app/.eslintignore new file mode 100644 index 00000000..35e915e5 --- /dev/null +++ b/packages/react-app/.eslintignore @@ -0,0 +1,3 @@ +**/node_modules/* +**/out/* +**/.next/* diff --git a/packages/react-app/.eslintrc.json b/packages/react-app/.eslintrc.json new file mode 100644 index 00000000..f9657030 --- /dev/null +++ b/packages/react-app/.eslintrc.json @@ -0,0 +1,42 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended" + // Uncomment the following lines to enable eslint-config-prettier + // Is not enabled right now to avoid issues with the Next.js repo + // "prettier", + ], + "env": { + "es6": true, + "browser": true, + "jest": true, + "node": true + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "react/react-in-jsx-scope": 0, + "react/display-name": 0, + "react/prop-types": 0, + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/explicit-member-accessibility": 0, + "@typescript-eslint/indent": 0, + "@typescript-eslint/member-delimiter-style": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/no-use-before-define": 0, + "@typescript-eslint/no-unused-vars": [ + 2, + { + "argsIgnorePattern": "^_" + } + ], + "react/no-unescaped-entities": 1 + } +} diff --git a/packages/react-app/.gitignore b/packages/react-app/.gitignore new file mode 100644 index 00000000..74b75863 --- /dev/null +++ b/packages/react-app/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/packages/react-app/.prettierignore b/packages/react-app/.prettierignore new file mode 100644 index 00000000..09c508e8 --- /dev/null +++ b/packages/react-app/.prettierignore @@ -0,0 +1,4 @@ +# Ignore : +build +.next +node_modules \ No newline at end of file diff --git a/packages/react-app/.prettierrc b/packages/react-app/.prettierrc new file mode 100644 index 00000000..b2095be8 --- /dev/null +++ b/packages/react-app/.prettierrc @@ -0,0 +1,4 @@ +{ + "semi": false, + "singleQuote": true +} diff --git a/packages/react-app/.prettierrc.js b/packages/react-app/.prettierrc.js new file mode 100644 index 00000000..907d02e3 --- /dev/null +++ b/packages/react-app/.prettierrc.js @@ -0,0 +1,10 @@ +module.exports = { + tabWidth: 2, + printWidth: 80, + endOfLine: 'auto', + arrowParens: 'always', + semi: true, + useTabs: false, + singleQuote: true, + bracketSpacing: true, +}; diff --git a/packages/react-app/docs/CHANGELOG.md b/packages/react-app/docs/CHANGELOG.md new file mode 100644 index 00000000..bcb8d9fc --- /dev/null +++ b/packages/react-app/docs/CHANGELOG.md @@ -0,0 +1,13 @@ +# CHANGELOG + +## 1.0.0-beta (2021-08-12) + +1. Add github and heroku actions integration, initialize react-create app +2. Import next.js bankless dao template +3. Add mongodb integration +4. Add netlify integration with configuration +5. Add mock json data for mongodb compass testing +6. Fix netlify deployment and add .github docs +7. Add webhook integration for posting bounties +8. Allow editing of OPEN bounties +9. Add HashId to bounties display, disable connect wallet, set dark mode as default, make logo a link diff --git a/packages/react-app/docs/REACT_START_HERE.md b/packages/react-app/docs/REACT_START_HERE.md new file mode 100644 index 00000000..02aac3f6 --- /dev/null +++ b/packages/react-app/docs/REACT_START_HERE.md @@ -0,0 +1,70 @@ +# Getting Started with Create React App + +This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). + +## Available Scripts + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.\ +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.\ +You will also see any lint errors in the console. + +### `yarn test` + +Launches the test runner in the interactive watch mode.\ +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + +### `yarn build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.\ +Your app is ready to be deployed! + +See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + +### `yarn eject` + +**Note: this is a one-way operation. Once you `eject`, you can’t go back!** + +If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + +Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. + +You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. + +## Learn More + +You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + +To learn React, check out the [React documentation](https://reactjs.org/). + +### Code Splitting + +This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + +### Analyzing the Bundle Size + +This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) + +### Making a Progressive Web App + +This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + +### Advanced Configuration + +This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) + +### Deployment + +This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) + +### `yarn build` fails to minify + +This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/packages/react-app/next-env.d.ts b/packages/react-app/next-env.d.ts new file mode 100644 index 00000000..c6643fda --- /dev/null +++ b/packages/react-app/next-env.d.ts @@ -0,0 +1,3 @@ +/// +/// +/// diff --git a/packages/react-app/next-seo.config.ts b/packages/react-app/next-seo.config.ts new file mode 100644 index 00000000..d90461e4 --- /dev/null +++ b/packages/react-app/next-seo.config.ts @@ -0,0 +1,32 @@ +const title = 'Bankless Bounty Board'; +const description = + 'Bankless Bounty Board'; +const url = 'https://bountyboard.bankless.community'; + +const SEO = { + title, + description, + canonical: url, + openGraph: { + type: 'website', + url, + title, + description, + images: [ + { + url: `https://www.bankless.community/logo.svg`, + alt: title, + width: 2048, + height: 1170, + }, + ], + }, + twitter: { + cardType: 'summary_large_image', + handle: '@banklessdao', + site: '@banklessdao', + }, + additionalLinkTags: [{ rel: 'icon', href: '/favicon.png' }], +}; + +export default SEO; diff --git a/packages/react-app/next.config.js b/packages/react-app/next.config.js new file mode 100644 index 00000000..83c12d0d --- /dev/null +++ b/packages/react-app/next.config.js @@ -0,0 +1,4 @@ +module.exports = { + distDir: 'dist', + webpack5: true, +} \ No newline at end of file diff --git a/packages/react-app/package.json b/packages/react-app/package.json new file mode 100644 index 00000000..4986f2d3 --- /dev/null +++ b/packages/react-app/package.json @@ -0,0 +1,68 @@ +{ + "name": "@bounty-board/react-app", + "version": "0.1.0", + "homepage": ".", + "description": "This web app displays the bounties that are available for DAO members to claim.", + "repository": "https://github.com/BanklessDAO/bounty-board", + "license": "MIT", + "bugs": { + "url": "https://github.com/BanklessDAO/bounty-board/issues" + }, + "eslintConfig": { + "extends": "react-app" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "build:qa": "env-cmd -f .env.qa next build", + "build:prod": "env-cmd -f .env.prod next build", + "type-check": "tsc --pretty --noEmit --skipLibCheck --esModuleInterop --strict --jsx preserve", + "export": "next export", + "lint": "next lint", + "format": "prettier --write src" + }, + "lint-staged": { + "*.@(ts|tsx)": [ + "yarn run type-check", + "yarn lint src --fix -d", + "yarn format" + ] + }, + "dependencies": { + "@chakra-ui/react": "^1.6.5", + "@emotion/react": "^11.1.5", + "@emotion/styled": "^11.3.0", + "@fontsource/lexend": "^4.5.0", + "@hookform/resolvers": "^2.7.1", + "@netlify/plugin-nextjs": "^3.8.0", + "@typescript-eslint/eslint-plugin": "^4.28.3", + "env-cmd": "^10.1.0", + "framer-motion": "^4.1.17", + "mongodb-client-encryption": "^1.2.6", + "mongoose": "^5.13.4", + "next": "^11.0.0", + "next-seo": "^4.26.0", + "nprogress": "^0.2.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-hook-form": "^7.12.2", + "react-icons": "^4.2.0", + "swr": "^0.5.6", + "yup": "^0.32.9" + }, + "devDependencies": { + "@types/node": "^16.4.7", + "@types/nprogress": "^0.2.0", + "@types/react": "^17.0.14", + "eslint": "^7.30.0", + "eslint-config-next": "^11.0.1", + "lint-staged": "^11.0.0", + "prettier": "^2.3.2", + "typescript": "^4.3.5" + }, + "engines": { + "yarn": "1.x", + "node": "~14.17.0" + } +} diff --git a/packages/react-app/public/favicon.png b/packages/react-app/public/favicon.png new file mode 100644 index 00000000..4bec56fd Binary files /dev/null and b/packages/react-app/public/favicon.png differ diff --git a/packages/react-app/public/logo.svg b/packages/react-app/public/logo.svg new file mode 100644 index 00000000..faf7750a --- /dev/null +++ b/packages/react-app/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/react-app/src/components/global/Footer/index.tsx b/packages/react-app/src/components/global/Footer/index.tsx new file mode 100644 index 00000000..35bf89ef --- /dev/null +++ b/packages/react-app/src/components/global/Footer/index.tsx @@ -0,0 +1,37 @@ +import { Box, Flex, HStack, Text } from '@chakra-ui/react' +import AccessibleLink from '../../../components/parts/AccessibleLink' +import ColorModeButton from '../../../components/parts/ColorModeButton' + +import { discordSupportChannelUrl } from '../../../constants/discordInfo' +import { feedbackUrl } from '../../../constants/discordInfo' + +const Footer = (): JSX.Element => { + return ( + + + {/* + Mirror Substack Discord Twitter Github + */} + + + Give us Feedback + + + + Need Help? + + + + + © {new Date().getFullYear()} Bankless DAO + + ) +} + +export default Footer diff --git a/packages/react-app/src/components/global/Header/Logo.tsx b/packages/react-app/src/components/global/Header/Logo.tsx new file mode 100644 index 00000000..f75d935c --- /dev/null +++ b/packages/react-app/src/components/global/Header/Logo.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import { Box, BoxProps, Image, Link as ChakraLink } from '@chakra-ui/react' +import Link from 'next/link' + +export default function Logo(props: BoxProps): JSX.Element { + return ( + + + + Bankless DAO + + + + ) +} diff --git a/packages/react-app/src/components/global/Header/index.tsx b/packages/react-app/src/components/global/Header/index.tsx new file mode 100644 index 00000000..1d7fae17 --- /dev/null +++ b/packages/react-app/src/components/global/Header/index.tsx @@ -0,0 +1,102 @@ +import React, { useState } from 'react' +import { Box, Flex, Text, Stack, useColorModeValue } from '@chakra-ui/react' + +import { RiMenuFill, RiCloseFill } from 'react-icons/ri' + +import Logo from './Logo' +import ThemeToggle from '../../parts/ThemeToggle' +import AccessibleLink from '../../parts/AccessibleLink' +import ColorModeButton from '../../parts/ColorModeButton' + +const CloseIcon = ({ color }: { color: string }) => ( + +) +const MenuIcon = ({ color }: { color: string }) => ( + +) + +const NavBar: React.FC = (props): JSX.Element => { + const [isOpen, setIsOpen] = useState(false) + const toggle = () => setIsOpen(!isOpen) + + return ( + + + + + + ) +} + +const MenuToggle = ({ + toggle, + isOpen, +}: { + toggle: VoidFunction + isOpen: boolean +}): JSX.Element => { + const fgColor = useColorModeValue('black', 'white') + return ( + + {isOpen ? : } + + ) +} + +const MenuItem = ({ + children, + to = '/', + newTab, + ...rest +}: { + children?: React.ReactNode + isLast?: boolean + to: string + newTab?: boolean +}): JSX.Element => ( + + + {children} + + +) + +const MenuLinks = ({ isOpen }: { isOpen: boolean }): JSX.Element => ( + + + {/*Bounty Board*/} + {/*TODO: enabled this with web3 integration */} + {/**/} + {/* Connect Wallet{' '}*/} + {/**/} + + Join DAO{' '} + + + + +) + +const NavBarContainer: React.FC = (props): JSX.Element => ( + + {props.children} + +) + +export default NavBar diff --git a/packages/react-app/src/components/global/SiteLayout/index.tsx b/packages/react-app/src/components/global/SiteLayout/index.tsx new file mode 100644 index 00000000..3d226dbd --- /dev/null +++ b/packages/react-app/src/components/global/SiteLayout/index.tsx @@ -0,0 +1,36 @@ +import { Box, Stack, Heading } from '@chakra-ui/react' +import { ReactNode } from 'react' + +import Header from '../Header' +import Footer from '../Footer' + +type LayoutProps = { + children: ReactNode + title: string +} + +const SiteLayout = ({ children, title }: LayoutProps): JSX.Element => { + return ( + <> +
+ + + + {title} + + {children} + + +