diff --git a/.eslintrc.json b/.eslintrc.json index f9d74c2..e0bb3f0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -38,6 +38,7 @@ "no-restricted-syntax": "off", "no-param-reassign": "off", "no-underscore-dangle": "off", + "react/require-default-props": "off", "consistent-return": "off", "class-methods-use-this": "off", "import/no-extraneous-dependencies": "off", diff --git a/apps/dashboard/src/assets/image2.svg b/apps/dashboard/src/assets/image2.svg new file mode 100644 index 0000000..85d92f9 --- /dev/null +++ b/apps/dashboard/src/assets/image2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/dashboard/src/components/group-card.tsx b/apps/dashboard/src/components/group-card.tsx index 85f54a2..a549902 100644 --- a/apps/dashboard/src/components/group-card.tsx +++ b/apps/dashboard/src/components/group-card.tsx @@ -7,74 +7,84 @@ import { Text, VStack } from "@chakra-ui/react" -import { Link } from "react-router-dom" import icon1Image from "../assets/icon1.svg" import icon2Image from "../assets/icon2.svg" import icon3Image from "../assets/icon3.svg" import icon4Image from "../assets/icon4.svg" -import { Group } from "../types" + +export type GroupCardProps = { + name?: string + type?: string + description?: string + members?: any[] + treeDepth?: number +} export default function GroupCard({ name, type, description, members, - treeDepth, - id -}: Group): JSX.Element { + treeDepth +}: GroupCardProps): JSX.Element { return ( - - - - - = 27 - ? icon4Image - : treeDepth >= 24 - ? icon3Image - : treeDepth >= 20 - ? icon2Image - : icon1Image - } - htmlWidth="35px" - alt="Bandada icon" - /> + + + + = 27 + ? icon4Image + : treeDepth >= 24 + ? icon3Image + : treeDepth >= 20 + ? icon2Image + : icon1Image + } + htmlWidth="35px" + alt="Bandada icon" + /> - - {type} - - + + {type || "on/off chain"} + + - - {name} - + + {name || "[untitled]"} + - - {description} - - + + {description || + (type !== "on-chain" && "[no description yet]")} + + - - {members.length} - members - + + {members?.length || 0} + members - + ) } diff --git a/apps/dashboard/src/data/groupSizes.ts b/apps/dashboard/src/data/groupSizes.ts index 0edf3c3..da7490a 100644 --- a/apps/dashboard/src/data/groupSizes.ts +++ b/apps/dashboard/src/data/groupSizes.ts @@ -1,30 +1,51 @@ -import { GroupSizes } from "../types" +import { GroupSize } from "../types" -const groupSizes: GroupSizes = { - small: { - description: "For communities, small teams", - capacity: "Capacity 65 thousand", - useCases: ["voting", "feedback"], +const groupSizes: GroupSize[] = [ + { + name: "Small", + description: "Communities and teams", + capacity: "Up to 65k members", + useCases: [ + "Donate to your community", + "Give event feedback", + "Prove demographic, criminal, or health information to an employer" + ], treeDepth: 16 }, - medium: { - description: "For cities, large teams", - capacity: "Capacity 1 million", - useCases: ["voting", "feedback"], + { + name: "Medium", + description: "Cities and companies", + capacity: "Up to 1M members", + useCases: [ + "Vote in your city's election", + "Share feedback about work", + "Prove professional certification", + "Apply for grants/subsidies" + ], treeDepth: 20 }, - large: { - description: "For nations", - capacity: "Capacity 33 Million", - useCases: ["voting", "feedback"], + { + name: "Large", + description: "Cities, corporations, countries", + capacity: "Up to 33M members", + useCases: [ + "Participate in a census", + "Vote in a national election", + "Donate to a campaign" + ], treeDepth: 25 }, - xl: { - description: "For multiple nations, contries", - capacity: "Capacity 1 Billion", - useCases: ["voting", "feedback"], + { + name: "XL", + description: "Large countries or multiple nations", + capacity: "Up to 1B members", + useCases: [ + "Prove passport status", + "Share health status with other countries", + "Sponsor a cause" + ], treeDepth: 30 } -} +] export default groupSizes diff --git a/apps/dashboard/src/pages/groups.tsx b/apps/dashboard/src/pages/groups.tsx index 7f163ad..bb64ddc 100644 --- a/apps/dashboard/src/pages/groups.tsx +++ b/apps/dashboard/src/pages/groups.tsx @@ -11,24 +11,23 @@ import { InputRightElement, Spinner, Text, - useDisclosure, VStack } from "@chakra-ui/react" import { useCallback, useContext, useEffect, useState } from "react" import { FiSearch } from "react-icons/fi" -import { getGroups as getOnchainGroups } from "../api/semaphoreAPI" +import { Link, useNavigate } from "react-router-dom" import { getGroups as getOffchainGroups } from "../api/bandadaAPI" -import CreateGroupModal from "../components/create-group-modal" +import { getGroups as getOnchainGroups } from "../api/semaphoreAPI" import GroupCard from "../components/group-card" import { AuthContext } from "../context/auth-context" import { Group } from "../types" export default function GroupsPage(): JSX.Element { const { admin } = useContext(AuthContext) - const createGroupModal = useDisclosure() const [_isLoading, setIsLoading] = useState(false) const [_groups, setGroups] = useState([]) const [_searchField, setSearchField] = useState("") + const navigate = useNavigate() useEffect(() => { ;(async () => { @@ -57,21 +56,6 @@ export default function GroupsPage(): JSX.Element { })() }, [admin]) - const addGroup = useCallback( - (group?: Group) => { - if (!group) { - createGroupModal.onClose() - - return - } - - setGroups([group, ..._groups]) - - createGroupModal.onClose() - }, - [_groups, createGroupModal] - ) - const filterGroup = useCallback( (group: Group) => group.name.toLowerCase().includes(_searchField.toLowerCase()), @@ -122,7 +106,7 @@ export default function GroupsPage(): JSX.Element { @@ -153,25 +137,15 @@ export default function GroupsPage(): JSX.Element { a.name < b.name ? -1 : a.name > b.name ? 1 : 0 ) .map((group) => ( - - - + + + + + ))} )} - - ) } diff --git a/apps/dashboard/src/pages/new-group.tsx b/apps/dashboard/src/pages/new-group.tsx new file mode 100644 index 0000000..8752afb --- /dev/null +++ b/apps/dashboard/src/pages/new-group.tsx @@ -0,0 +1,502 @@ +import { validators } from "@bandada/reputation" +import { + Box, + Button, + Container, + Heading, + HStack, + Icon, + Image, + Input, + ListItem, + Select, + Tag, + Text, + UnorderedList, + VStack +} from "@chakra-ui/react" +import { useState } from "react" +import { FiHardDrive, FiZap } from "react-icons/fi" +import { MdOutlineKeyboardArrowRight } from "react-icons/md" +import { useNavigate } from "react-router-dom" +import icon1Image from "../assets/icon1.svg" +import icon2Image from "../assets/icon2.svg" +import icon3Image from "../assets/icon3.svg" +import icon4Image from "../assets/icon4.svg" +import image2 from "../assets/image2.svg" +import GroupCard from "../components/group-card" +import { groupSizes } from "../data" +import capitalize from "../utils/capitalize" + +const steps = ["General info", "Group size", "Member mechanism", "Summary"] +const groupTypes = ["on-chain", "off-chain"] +const accessModes = ["manual", "credentials"] + +export default function NewGroupPage(): JSX.Element { + const [_currentStep, setCurrentStep] = useState(0) + const [_groupName, setGroupName] = useState("") + const [_groupDescription, setGroupDescription] = useState() + const [_groupType, setGroupType] = useState<"off-chain" | "on-chain">() + const [_treeDepth, setTreeDepth] = useState() + const [_accessMode, setAccessMode] = useState<"manual" | "credentials">() + const navigate = useNavigate() + + return ( + + + + + Nueva bandada + + + + + {steps.map((step, i) => ( + setCurrentStep(i) + : undefined + } + cursor={i < _currentStep ? "pointer" : "inherit"} + color={ + i === _currentStep + ? "balticSea.800" + : "balticSea.500" + } + key={step} + > + + {i + 1} + + {step} + {i !== steps.length - 1 && ( + + )} + + ))} + + + + + + + + Group preview + + + + + + + + {_currentStep === 0 ? ( + <> + What type of group is this? + + + {groupTypes.map((groupType: any) => ( + + setGroupType(groupType) + } + key={groupType} + > + + + + + {groupType === "on-chain" + ? "On chain" + : "Off chain"} + + + + + Quick summary of pros and cons + of on-chain groups, spanning + about 3 lines? + + + ))} + + + + Name + + + setGroupName(event.target.value) + } + /> + + Give it a cool name you can recognize. + + + + {_groupType === "off-chain" && ( + + Description + + + setGroupDescription( + event.target.value + ) + } + /> + + Describe your group. + + + )} + + {_groupType && ( + + + {_groupType === "off-chain" + ? "By continuing, you will create that will be stored in our servers." + : "By continuing, you will create that lives on the Ethereum blockchain."} + + + )} + + ) : _currentStep === 1 ? ( + <> + How big is your group? + + + {groupSizes.map((groupSize) => ( + + setTreeDepth( + groupSize.treeDepth + ) + } + key={groupSize.name} + > + = 27 + ? icon4Image + : groupSize.treeDepth >= + 24 + ? icon3Image + : groupSize.treeDepth >= + 20 + ? icon2Image + : icon1Image + } + htmlWidth="50px" + alt="Bandada icon" + /> + + + {groupSize.name} + + + + {groupSize.capacity} + + + + {groupSize.description} + + + + Anonymously: + + + {groupSize.useCases.map( + (useCase) => ( + + {useCase} + + ) + )} + + + ))} + + + ) : ( + <> + + {accessModes.map((accessMode: any) => ( + + setAccessMode(accessMode) + } + key={accessMode} + > + + + + + {capitalize(accessMode)} + + + + + {accessMode === "manual" + ? "I’ll add members by pasting in their address or sending them a generated invite link." + : "Members can join my group if they fit the criteria I will setup."} + + + ))} + + + {_accessMode === "credentials" && ( + <> + + + Choose credential and provider + + + + + + + + Disclaimer: We will use a bit of + your member’s data to check if + they meet the criteria and + generate their reputation to + join the group. + + + + )} + + )} + + + + + + + + + + ) +} diff --git a/apps/dashboard/src/routes.tsx b/apps/dashboard/src/routes.tsx index 5fb91dc..dd91211 100644 --- a/apps/dashboard/src/routes.tsx +++ b/apps/dashboard/src/routes.tsx @@ -6,6 +6,7 @@ import NotFoundPage from "./pages/404" import GroupPage from "./pages/group" import GroupsPage from "./pages/groups" import HomePage from "./pages/home" +import NewGroupPage from "./pages/new-group" import ReputationPage from "./pages/reputation" export default function Routes(): JSX.Element { @@ -35,6 +36,10 @@ export default function Routes(): JSX.Element { path: "groups", element: }, + { + path: "groups/new", + element: + }, { path: "groups/:groupType/:groupId", element: diff --git a/apps/dashboard/src/types/Group.ts b/apps/dashboard/src/types/Group.ts index cb3f058..5f85b6e 100644 --- a/apps/dashboard/src/types/Group.ts +++ b/apps/dashboard/src/types/Group.ts @@ -13,12 +13,11 @@ export type Group = { } export type GroupSize = { + name: string description: string capacity: string useCases: string[] treeDepth: number } -export type GroupSizes = Record - export default Group diff --git a/apps/dashboard/src/utils/capitalize.ts b/apps/dashboard/src/utils/capitalize.ts new file mode 100644 index 0000000..3a64130 --- /dev/null +++ b/apps/dashboard/src/utils/capitalize.ts @@ -0,0 +1,3 @@ +export default function capitalize(s: string) { + return s[0].toUpperCase() + s.substring(1) +}