diff --git a/_data/pages/dapps.yml b/_data/pages/dapps.yml index 2e44f7d316..0f5d47532c 100644 --- a/_data/pages/dapps.yml +++ b/_data/pages/dapps.yml @@ -7,7 +7,11 @@ parent_page: /ecosystem/ breadcrumbs: false page_last_updated: true blocks: + - type: hero + variant: block_explorers + title: Hundreds of dApps, Services & Wallets + description: Immerse yourself in the Starknet Ecosystem by discovering projects, jobs, metrics and learning resources. - type: dapps variant: dapps title: dApps - description: Discover apps in the Starknet ecosystem across NFTs, Gaming, Defi, \ No newline at end of file + description: "" \ No newline at end of file diff --git a/workspaces/cms-scripts/src/index.ts b/workspaces/cms-scripts/src/index.ts index a9ef0708ec..585dd19e61 100644 --- a/workspaces/cms-scripts/src/index.ts +++ b/workspaces/cms-scripts/src/index.ts @@ -317,7 +317,19 @@ export interface TagObject { label: string; slug: string; } -const blackListTags = ["all"]; +const blackListTags = [ + "all", + "governance", + "pfp", + "green finance", + "cairo", + "formal-verification", + "dex, wallet, multi-chain, cross-chain, okx, bridge, blockchain", + "staking", + "access node", + "data", + "starkware", +]; const slugifyTags = (objects: Project[]): Project[] => { return objects.map((obj) => { @@ -345,7 +357,9 @@ const extractTags = (projects: Project[]): TagObject[] => { })); }; const slugifyDApps = slugifyTags(dAppsData.content); -const categories = extractTags(dAppsData.content); +const categories = extractTags(dAppsData.content).sort((a, b) => + a.label > b.label ? 1 : b.label > a.label ? -1 : 0 +); await fs.mkdir("public/data/starknet-db-projects-dapps", { recursive: true }); await write( diff --git a/workspaces/website/src/pages/starknet-db-projects-dapps/(components)/DappsPage.tsx b/workspaces/website/src/pages/starknet-db-projects-dapps/(components)/DappsPage.tsx index b865baf261..3c27017596 100644 --- a/workspaces/website/src/pages/starknet-db-projects-dapps/(components)/DappsPage.tsx +++ b/workspaces/website/src/pages/starknet-db-projects-dapps/(components)/DappsPage.tsx @@ -6,6 +6,8 @@ import { List, ListItem, HStack, + Circle, + Text, } from "@chakra-ui/react"; import { DappsProps, @@ -13,13 +15,15 @@ import { } from "@starknet-io/cms-data/src/starknet-db-projects-dapps"; import { Button } from "@ui/Button"; import { useEffect, useMemo, useState } from "react"; -import { Input, Select } from "@chakra-ui/react"; +import { Input } from "@chakra-ui/react"; import { navigate } from "vite-plugin-ssr/client/router"; import useQueryString from "src/hooks/useQueryString"; +import { IoSearchOutline } from "react-icons/io5"; enum SORT_BY { - A_Z = "A-Z", - FOLLOWERS = "Followers", + ALL = "All", + MAINNET = "Mainnet", + TESTNET = "Testnet", } const DappsPage = ({ list, categories }: DappsProps) => { @@ -51,7 +55,9 @@ const DappsPage = ({ list, categories }: DappsProps) => { sortBy && queryParams.set("sortBy", sortBy); searchValue && queryParams.set("searchValue", searchValue); - navigate(`${url.pathname}?${queryParams.toString()}`); + navigate(`${url.pathname}?${queryParams.toString()}`, { + keepScrollPosition: true, + }); }, [searchValue, sortBy, selectedCategory, url.pathname]); const projects = useMemo(() => { const byCategory = @@ -65,12 +71,11 @@ const DappsPage = ({ list, categories }: DappsProps) => { }, [selectedCategory, searchValue]); const projectsSort = useMemo(() => { - if (sortBy === SORT_BY.A_Z) - return projects.sort((a, b) => a.name.localeCompare(b.name)); - return projects.sort( - (a, b) => - a.socialMetrics?.twitterFollower - b.socialMetrics?.twitterFollower - ); + if (sortBy === SORT_BY.MAINNET) + return projects.filter((project) => project.isLive); + if (sortBy === SORT_BY.TESTNET) + return projects.filter((project) => project.isTestnetLive); + else return projects; }, [sortBy, projects]); const handleChangeCategory = (category: string | undefined) => { @@ -81,13 +86,22 @@ const DappsPage = ({ list, categories }: DappsProps) => { return ( <> <> - <Heading>Dapps</Heading> <Stack display="flex" direction="row" gap="100px"> <List display="flex" flexDir="column" gap="5px"> - <ListItem> + <Heading color="#0C0C4F" fontSize={18} fontWeight={600}> + Categories + </Heading> + <ListItem display="flex"> + {selectedCategory === undefined && ( + <Box w="2px" h="40px" bgGradient="linear(#EC796B,#D672EF)" /> + )} <Button - variant="filter" - sx={{ bgColor: selectedCategory === undefined ? "red" : "" }} + sx={{ + color: selectedCategory === undefined ? "#0C0C4F" : "#6B7280", + fontSize: 14, + fontWeight: 400, + }} + variant="none" onClick={() => handleChangeCategory(undefined)} > All @@ -95,44 +109,106 @@ const DappsPage = ({ list, categories }: DappsProps) => { </ListItem> {categories.map((category) => { return ( - <ListItem key={category.slug}> + <ListItem key={category.slug} display="flex"> + {selectedCategory === category && ( + <Box + w="2px" + h="40px" + bgGradient="linear(#EC796B,#D672EF)" + /> + )} <Button + variant="none" + sx={{ + color: + selectedCategory === category ? "#0C0C4F" : "#6B7280", + fontSize: 14, + fontWeight: 400, + }} onClick={() => handleChangeCategory(category.slug)} - variant={"filter"} - sx={{ bgColor: selectedCategory === category ? "red" : "" }} > - {category.label} + {category.label.charAt(0).toUpperCase() + + category.label.slice(1).replace("_", " ")} </Button> </ListItem> ); })} </List> - <Box> - <HStack> - <Box>List length:{projects.length}</Box> - <Select - value={sortBy || ""} - variant="filled" - onChange={(e) => setSortBy(e.target.value)} + <Box w="100%"> + <HStack justifyContent="space-between"> + <Box> + <HStack gap="8px"> + {Object.values(SORT_BY).map((sortByItem) => ( + <Button + variant="none" + fontSize={12} + px="16px" + py="8px" + bgColor={ + sortBy === sortByItem || + (sortByItem === SORT_BY.ALL && !sortBy) + ? "#0C0C4F" + : "#F6F6F6" + } + color={ + sortBy === sortByItem || + (sortByItem === SORT_BY.ALL && !sortBy) + ? "#FFFFFF" + : "#6B7280" + } + onClick={() => setSortBy(sortByItem)} + > + {sortByItem} + </Button> + ))} + </HStack> + </Box> + <HStack + bgColor="white" + px="18px" + py="4px" + borderRadius="8px" + cursor="pointer" > - {Object.values(SORT_BY).map((sortByItem) => ( - <option value={sortByItem} key={sortByItem}> - {sortByItem} - </option> - ))} - </Select> - <Input - defaultValue={searchValue} - placeholder="Search" - onChange={(e) => setSearchValue(e.target.value)} - /> + <IoSearchOutline size={20} /> + <Input + border="none" + p="0" + outline="none" + _focus={{ border: "none" }} + _focusVisible={{ border: "none" }} + cursor="pointer" + bgColor="transparent" + fontSize="14px" + defaultValue={searchValue} + placeholder="Search" + onChange={(e) => setSearchValue(e.target.value)} + /> + </HStack> </HStack> - <List display="flex" flexWrap="wrap" gap="20px"> + <List display="flex" flexWrap="wrap" gap="20px" mt="40px"> {projectsSort.map((item) => { return ( - <ListItem width="300px" key={item.name}> - <Box>{item.name}</Box> - <Image src={item.network.twitterBanner} loading="lazy" /> + <ListItem + width={{ base: "343px", sm: "373px", md: "323px" }} + height="286px" + bgColor="white" + padding="32px" + key={item.name} + borderRadius="8px" + > + <Circle size="56px" bgColor="red" mb={3}> + <Image + src={item.network.twitterImage} + loading="lazy" + fit="fill" + borderRadius="100%" + /> + </Circle> + <Text noOfLines={1} mb="8px" fontSize={18} fontWeight={600}> + {item.name} + </Text> + <Text noOfLines={2}>{item.description}</Text> </ListItem> ); })}