diff --git a/apps/tangle-dapp/app/blueprints/BlueprintListing.tsx b/apps/tangle-dapp/app/blueprints/BlueprintListing.tsx new file mode 100644 index 0000000000..ca8f210ac0 --- /dev/null +++ b/apps/tangle-dapp/app/blueprints/BlueprintListing.tsx @@ -0,0 +1,344 @@ +'use client'; + +import { + createColumnHelper, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { SparklingIcon } from '@webb-tools/icons'; +import Button from '@webb-tools/webb-ui-components/components/buttons/Button'; +import { fuzzyFilter } from '@webb-tools/webb-ui-components/components/Filter/utils'; +import { Input } from '@webb-tools/webb-ui-components/components/Input'; +import { Pagination } from '@webb-tools/webb-ui-components/components/Pagination'; +import { Typography } from '@webb-tools/webb-ui-components/typography/Typography'; +import { shortenHex } from '@webb-tools/webb-ui-components/utils/shortenHex'; +import Image from 'next/image'; +import { FC, useMemo, useState } from 'react'; +import { twMerge } from 'tailwind-merge'; + +import { Blueprint, BlueprintCategory } from '../../types'; +import useBlueprintListing from './useBlueprintListing'; + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor('address', { + header: () => 'Project', + cell: (props) => , + filterFn: (row, _, filterValue) => { + const { name, address, description } = row.original; + return ( + name.toLowerCase().includes(filterValue.toLowerCase()) || + address.toLowerCase().includes(filterValue.toLowerCase()) || + description.toLowerCase().includes(filterValue.toLowerCase()) + ); + }, + sortingFn: (rowA, rowB) => { + const isBlueprintABoosted = rowA.original.isBoosted; + const isBlueprintBBoosted = rowB.original.isBoosted; + if (isBlueprintABoosted && !isBlueprintBBoosted) { + return -1; + } + if (!isBlueprintABoosted && isBlueprintBBoosted) { + return 1; + } + return 0; + }, + }), + columnHelper.accessor('category', { + header: () => null, + cell: () => null, + filterFn: (row, _, filterValue) => { + return row.original.category === filterValue; + }, + }), +]; + +const BlueprintListing: FC = () => { + const blueprints = useBlueprintListing(); + const [searchValue, setSearchValue] = useState(''); + const [filteredCategory, setFilteredCategory] = + useState(null); + + const categoryItems = useMemo( + () => [ + { + label: 'View All', + onClick: () => setFilteredCategory(null), + isActive: filteredCategory === null, + }, + ...Object.values(BlueprintCategory).map((category) => ({ + label: category, + onClick: () => setFilteredCategory(category), + isActive: filteredCategory === category, + })), + ], + [filteredCategory], + ); + + const table = useReactTable({ + data: blueprints, + columns, + filterFns: { + fuzzy: fuzzyFilter, + }, + globalFilterFn: fuzzyFilter, + initialState: { + columnVisibility: { + category: false, + }, + pagination: { + pageSize: 12, + }, + sorting: [ + { + id: 'address', + desc: false, + }, + ], + }, + state: { + columnFilters: [ + { + id: 'address', + value: searchValue, + }, + ...(filteredCategory + ? [ + { + id: 'category', + value: filteredCategory, + }, + ] + : []), + ], + }, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + autoResetPageIndex: false, + }); + + return ( +
+ {/* Search bar */} +
+ setSearchValue(val)} + isControlled + className="flex-1 rounded-full overflow-hidden" + inputClassName="border-0 bg-mono-20 dark:bg-mono-200" + /> + +
+ + {/* Category */} +
+
+ {categoryItems.map(({ label, onClick, isActive }, idx) => ( +
+ + {label} + +
+ ))} +
+
+
+ + {/* Blueprint list */} +
+ {table.getRowModel().rows.map((row) => ( +
+ {row + .getVisibleCells() + .map((cell) => + flexRender(cell.column.columnDef.cell, cell.getContext()), + )} +
+ ))} +
+ + {/* Pagination */} + +
+ ); +}; + +export default BlueprintListing; + +const BlueprintItem: FC = ({ + name, + address, + imgUrl, + description, + restakersCount, + operatorsCount, + tvl, + isBoosted, +}) => { + return ( +
+ {isBoosted && ( +
+ )} +
+
+
+ {name} +
+
+
+ + {name} + +
+ {isBoosted && ( +
+ + + Boosted + +
+ )} +
+ + {shortenHex(address)} + +
+
+ + + {formatDescription(description)} + +
+ +
+
+ + Restakers + + {restakersCount} +
+
+ + Operators + + {operatorsCount} +
+
+ + TVL + + {tvl} +
+
+
+
+ ); +}; + +function formatDescription(description: string): string { + const maxLength = 200; + const ellipsis = '...'; + + if (description.length > maxLength + ellipsis.length) { + return description.slice(0, maxLength) + ellipsis; + } + + return description; +} diff --git a/apps/tangle-dapp/app/blueprints/TopBanner.tsx b/apps/tangle-dapp/app/blueprints/TopBanner.tsx new file mode 100644 index 0000000000..b30bf54bb2 --- /dev/null +++ b/apps/tangle-dapp/app/blueprints/TopBanner.tsx @@ -0,0 +1,33 @@ +import { ArrowRight } from '@webb-tools/icons'; +import Button from '@webb-tools/webb-ui-components/components/buttons/Button'; +import { Typography } from '@webb-tools/webb-ui-components/typography/Typography'; +import { FC } from 'react'; +import { twMerge } from 'tailwind-merge'; + +const TopBanner: FC = () => { + return ( +
+
+ + Create your first{' '} + Blueprint + + + Set up a minimal Tangle Blueprint in minutes accompanied by a + step-by-step guide. + + +
+
+ ); +}; + +export default TopBanner; diff --git a/apps/tangle-dapp/app/blueprints/page.tsx b/apps/tangle-dapp/app/blueprints/page.tsx new file mode 100644 index 0000000000..a0f6167b1a --- /dev/null +++ b/apps/tangle-dapp/app/blueprints/page.tsx @@ -0,0 +1,24 @@ +import { Metadata } from 'next'; +import { FC } from 'react'; + +import createPageMetadata from '../../utils/createPageMetadata'; +import BlueprintListing from './BlueprintListing'; +import TopBanner from './TopBanner'; + +export const dynamic = 'force-static'; + +export const metadata: Metadata = createPageMetadata({ + title: 'Blueprints', +}); + +const BlueprintsPage: FC = () => { + return ( +
+ + + +
+ ); +}; + +export default BlueprintsPage; diff --git a/apps/tangle-dapp/app/blueprints/useBlueprintListing.ts b/apps/tangle-dapp/app/blueprints/useBlueprintListing.ts new file mode 100644 index 0000000000..e1661d9288 --- /dev/null +++ b/apps/tangle-dapp/app/blueprints/useBlueprintListing.ts @@ -0,0 +1,184 @@ +'use client'; + +import { Blueprint, BlueprintCategory } from '../../types'; + +export default function useBlueprintListing(): Blueprint[] { + return [ + { + name: 'Groth16 ZK-SaaS', + address: '0x1234567890123456789012345678901234567890', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'A ZK-SaaS service utilizing the Groth16 proving system for efficient and secure zero-knowledge proofs. Ideal for applications requiring fast verification times.', + restakersCount: 10, + operatorsCount: 2, + tvl: '$100', + isBoosted: true, + }, + { + name: 'Plonk Prover Pro', + address: '0x2345678901234567890123456789012345678901', + category: BlueprintCategory.CATEGORY_2, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Advanced PLONK-based ZK proof generation.', + restakersCount: 15, + operatorsCount: 3, + tvl: '$150', + isBoosted: true, + }, + { + name: 'Sonic ZK Solutions', + address: '0x3456789012345678901234567890123456789012', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'High-speed ZK-SNARK service leveraging the Sonic protocol. Our platform offers scalable and efficient zero-knowledge proofs for a wide range of applications, from privacy-preserving transactions to secure data verification.', + restakersCount: 8, + operatorsCount: 2, + tvl: '$80', + isBoosted: true, + }, + { + name: 'Marlin ZK Platform', + address: '0x4567890123456789012345678901234567890123', + category: BlueprintCategory.CATEGORY_3, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Scalable ZK proof generation using Marlin.', + restakersCount: 12, + operatorsCount: 4, + tvl: '$120', + }, + { + name: 'Bulletproofs ZK Service', + address: '0x5678901234567890123456789012345678901234', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'Efficient ZK range proofs using Bulletproofs technology. Our service provides compact zero-knowledge proofs without a trusted setup, making it ideal for cryptocurrency applications and privacy-preserving smart contracts.', + restakersCount: 6, + operatorsCount: 1, + tvl: '$60', + }, + { + name: 'ZK-STARK Engine', + address: '0x6789012345678901234567890123456789012345', + category: BlueprintCategory.CATEGORY_3, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Scalable ZK-STARK proof generation service.', + restakersCount: 20, + operatorsCount: 5, + tvl: '$200', + }, + { + name: 'Ligero ZK Platform', + address: '0x7890123456789012345678901234567890123456', + category: BlueprintCategory.CATEGORY_3, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'Lightweight ZK proof service using the Ligero protocol. We offer fast and efficient zero-knowledge proofs suitable for IoT devices and resource-constrained environments.', + restakersCount: 7, + operatorsCount: 2, + tvl: '$70', + }, + { + name: 'Aurora ZK Cloud', + address: '0x8901234567890123456789012345678901234567', + category: BlueprintCategory.CATEGORY_2, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Cloud-based ZK proofs.', + restakersCount: 18, + operatorsCount: 4, + tvl: '$180', + }, + { + name: 'Fractal ZK Services', + address: '0x9012345678901234567890123456789012345678', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'Recursive ZK proof generation using fractal architecture. Our innovative approach allows for highly scalable and efficient proofs, enabling complex computations to be verified quickly and securely.', + restakersCount: 14, + operatorsCount: 3, + tvl: '$140', + }, + { + name: 'Halo2 ZK Platform', + address: '0xa123456789012345678901234567890123456789', + category: BlueprintCategory.CATEGORY_2, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'ZK proofs with Halo2 system.', + restakersCount: 16, + operatorsCount: 3, + tvl: '$160', + }, + { + name: 'Nova ZK Accelerator', + address: '0xb234567890123456789012345678901234567890', + category: BlueprintCategory.CATEGORY_2, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'High-performance ZK proof generation leveraging the Nova protocol. We specialize in accelerating zero-knowledge computations for blockchain scaling solutions and privacy-preserving applications.', + restakersCount: 11, + operatorsCount: 2, + tvl: '$110', + }, + { + name: 'Hydra ZK Network', + address: '0xc345678901234567890123456789012345678901', + category: BlueprintCategory.CATEGORY_3, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Distributed ZK proofs.', + restakersCount: 25, + operatorsCount: 6, + tvl: '$250', + }, + { + name: 'Quantum ZK Solutions', + address: '0xd456789012345678901234567890123456789012', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'Next-generation ZK proofs leveraging quantum-resistant algorithms. Our cutting-edge service ensures long-term security for zero-knowledge applications, protecting against potential threats from quantum computing advancements.', + restakersCount: 9, + operatorsCount: 2, + tvl: '$90', + }, + { + name: 'Mina ZK Provider', + address: '0xe567890123456789012345678901234567890123', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: 'Lightweight ZK proofs with Mina.', + restakersCount: 13, + operatorsCount: 3, + tvl: '$130', + }, + { + name: 'ZK Rollup Express', + address: '0xf678901234567890123456789012345678901234', + category: BlueprintCategory.CATEGORY_1, + imgUrl: + 'https://images.unsplash.com/photo-1641194255129-bd39dd8112de?q=80&w=2380&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + description: + 'Specialized ZK proof service for Layer 2 rollups. Our platform enables high-throughput, low-cost transactions on Ethereum by leveraging zero-knowledge proofs to compress and verify batches of transactions off-chain.', + restakersCount: 22, + operatorsCount: 5, + tvl: '$220', + }, + ]; +} diff --git a/apps/tangle-dapp/components/Breadcrumbs/utils.tsx b/apps/tangle-dapp/components/Breadcrumbs/utils.tsx index 49f0d2a474..5b30136d1b 100644 --- a/apps/tangle-dapp/components/Breadcrumbs/utils.tsx +++ b/apps/tangle-dapp/components/Breadcrumbs/utils.tsx @@ -22,6 +22,7 @@ const BREADCRUMB_ICONS: Record JSX.Element> = { [PagePath.CLAIM_AIRDROP]: GiftLineIcon, [PagePath.ACCOUNT]: UserFillIcon, [PagePath.NOMINATION]: CoinLine, + [PagePath.BLUEPRINTS]: GridFillIcon, [PagePath.SERVICES]: GridFillIcon, [PagePath.RESTAKE]: TokenSwapFill, [PagePath.RESTAKE_DEPOSIT]: TokenSwapFill, diff --git a/apps/tangle-dapp/components/Sidebar/sidebarProps.ts b/apps/tangle-dapp/components/Sidebar/sidebarProps.ts index df47c38d72..2da9981e03 100644 --- a/apps/tangle-dapp/components/Sidebar/sidebarProps.ts +++ b/apps/tangle-dapp/components/Sidebar/sidebarProps.ts @@ -7,6 +7,7 @@ import { DocumentationIcon, GiftLineIcon, GlobalLine, + GridFillIcon, PolkadotJs, ShuffleLine, TokenSwapFill, @@ -47,6 +48,15 @@ const SIDEBAR_STATIC_ITEMS: SideBarItemProps[] = [ subItems: [], environments: ['development', 'staging', 'test'], }, + { + name: 'Blueprints', + href: PagePath.BLUEPRINTS, + isInternal: true, + isNext: true, + Icon: GridFillIcon, + subItems: [], + environments: ['development', 'staging', 'test'], + }, { name: 'Restake', // The default restake page is the deposit page. diff --git a/apps/tangle-dapp/public/static/assets/bg-dark.jpeg b/apps/tangle-dapp/public/static/assets/bg-dark.jpeg deleted file mode 100644 index 53d5ef5ed1..0000000000 Binary files a/apps/tangle-dapp/public/static/assets/bg-dark.jpeg and /dev/null differ diff --git a/apps/tangle-dapp/public/static/assets/bg.jpeg b/apps/tangle-dapp/public/static/assets/bg.jpeg deleted file mode 100644 index 9e2ce9267f..0000000000 Binary files a/apps/tangle-dapp/public/static/assets/bg.jpeg and /dev/null differ diff --git a/apps/tangle-dapp/public/static/assets/blueprints/grid-bg-dark.png b/apps/tangle-dapp/public/static/assets/blueprints/grid-bg-dark.png new file mode 100644 index 0000000000..06d76010cf Binary files /dev/null and b/apps/tangle-dapp/public/static/assets/blueprints/grid-bg-dark.png differ diff --git a/apps/tangle-dapp/public/static/assets/blueprints/grid-bg.png b/apps/tangle-dapp/public/static/assets/blueprints/grid-bg.png new file mode 100644 index 0000000000..44a32e4b83 Binary files /dev/null and b/apps/tangle-dapp/public/static/assets/blueprints/grid-bg.png differ diff --git a/apps/tangle-dapp/public/static/assets/blueprints/top-banner-dark.png b/apps/tangle-dapp/public/static/assets/blueprints/top-banner-dark.png new file mode 100644 index 0000000000..fe460715e2 Binary files /dev/null and b/apps/tangle-dapp/public/static/assets/blueprints/top-banner-dark.png differ diff --git a/apps/tangle-dapp/public/static/assets/blueprints/top-banner.png b/apps/tangle-dapp/public/static/assets/blueprints/top-banner.png new file mode 100644 index 0000000000..c3333e7fb4 Binary files /dev/null and b/apps/tangle-dapp/public/static/assets/blueprints/top-banner.png differ diff --git a/apps/tangle-dapp/types/index.ts b/apps/tangle-dapp/types/index.ts index 95c516f39d..32baf7af18 100755 --- a/apps/tangle-dapp/types/index.ts +++ b/apps/tangle-dapp/types/index.ts @@ -3,6 +3,7 @@ import type { SpStakingPagedExposureMetadata, } from '@polkadot/types/lookup'; import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; import type { WebbProviderType } from '@webb-tools/abstract-api-provider/types'; export enum PagePath { @@ -10,6 +11,7 @@ export enum PagePath { CLAIM_AIRDROP = '/claim', ACCOUNT = '/', BRIDGE = '/bridge', + BLUEPRINTS = '/blueprints', SERVICES = '/services', RESTAKE = '/restake', RESTAKE_DEPOSIT = '/restake/deposit', @@ -239,3 +241,22 @@ export type TangleTokenSymbol = 'tTNT' | 'TNT'; * @returns The success message. */ export type GetSuccessMessageFunction = (context: Context) => string; + +// add Blueprint +export type Blueprint = { + name: string; + address: HexString; + imgUrl: string; + category: BlueprintCategory; + description: string; + restakersCount: number; + operatorsCount: number; + tvl: string; + isBoosted?: boolean; +}; + +export enum BlueprintCategory { + CATEGORY_1 = 'Category 1', + CATEGORY_2 = 'Category 2', + CATEGORY_3 = 'Category 3', +} diff --git a/next.config.cjs b/next.config.cjs index a66e79c37b..9a6580c4ba 100644 --- a/next.config.cjs +++ b/next.config.cjs @@ -33,6 +33,9 @@ const nextConfig = { }, ]; }, + images: { + domains: ['images.unsplash.com'], // TODO: update when adding api for blueprints + }, }; module.exports = nextConfig;