From 7a8a42a25df3727e915b486574d174dce0229a26 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Tue, 9 Apr 2024 03:46:32 +0100 Subject: [PATCH] feat: implement homepage --- LICENSE | 4 +- README.md | 9 ++ apps/web/src/assets/anchor.png | Bin 0 -> 7971 bytes apps/web/src/assets/graphql.png | Bin 0 -> 3709 bytes apps/web/src/assets/mantine.svg | 8 + apps/web/src/assets/nestjs.svg | 1 + apps/web/src/assets/prisma.svg | 6 + apps/web/src/assets/react.svg | 8 + apps/web/src/index.html | 3 + .../generate-business-visa-image.tsx | 4 - .../src/lib/api-preset-data-user.service.ts | 3 +- .../src/lib/api-preset-minter.service.ts | 26 ++- libs/sdk/src/generated/graphql-sdk.ts | 34 ++++ libs/sdk/src/graphql/feature-preset.graphql | 4 + .../src/lib/user-asset-detail-feature.tsx | 11 +- .../src/lib/asset-activity-ui-entry-list.tsx | 12 +- ...ser-community-detail-minter-detail-tab.tsx | 8 +- .../user-community-detail-minter-list-tab.tsx | 18 ++- .../community/ui/src/lib/minter-ui-list.tsx | 34 +++- libs/web/home/feature/src/lib/home-routes.tsx | 3 +- .../home/feature/src/lib/pages/about-page.tsx | 20 --- .../home/feature/src/lib/pages/home-page.tsx | 67 ++++---- .../homepage-features.module.css | 24 +++ .../homepage-features/homepage-features.tsx | 99 ++++++++++++ .../homepage-footer.module.css | 89 +++++++++++ .../ui/homepage-footer/homepage-footer.tsx | 106 ++++++++++++ .../homepage-header.module.css | 32 ++++ .../ui/homepage-header/homepage-header.tsx | 57 +++++++ .../ui/homepage-hero/homepage-hero.module.css | 61 +++++++ .../lib/ui/homepage-hero/homepage-hero.tsx | 53 ++++++ .../ui/homepage-pricing/homepage-pricing.tsx | 151 ++++++++++++++++++ .../homepage-technology.module.css | 24 +++ .../homepage-technology.tsx | 101 ++++++++++++ .../homepage-use-cases.module.css | 47 ++++++ .../homepage-use-cases/homepage-use-cases.tsx | 60 +++++++ 35 files changed, 1116 insertions(+), 71 deletions(-) create mode 100644 apps/web/src/assets/anchor.png create mode 100644 apps/web/src/assets/graphql.png create mode 100644 apps/web/src/assets/mantine.svg create mode 100644 apps/web/src/assets/nestjs.svg create mode 100644 apps/web/src/assets/prisma.svg create mode 100644 apps/web/src/assets/react.svg delete mode 100644 libs/web/home/feature/src/lib/pages/about-page.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-pricing/homepage-pricing.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.tsx create mode 100644 libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.module.css create mode 100644 libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.tsx diff --git a/LICENSE b/LICENSE index 1d57124..6c3e54b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License -Copyright (c) 2023 Bram Borggreve https://github.com/beeman +Copyright (c) 2024 Bram Borggreve https://github.com/beeman +Copyright (c) 2024 Sundeep Charan Ramkumar https://github.com/sunguru98 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e773bbf..529eff3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # TokenGator +> The Easiest Way to Manage Dynamic NFT Collections on Solana. + +## Repositories + +TokenGator is currently split into two repositories, one for the platform (API/SDK/Web UI) and one for the Anchor program. + +- [TokenGator Platform](https://github.com/pubkeyapp/tokengator) (this repository) +- [TokenGator Program Library](https://github.com/pubkeyapp/tokengator-program-library) + ## Getting Started ### Prerequisites diff --git a/apps/web/src/assets/anchor.png b/apps/web/src/assets/anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..e4378ddfce82a9728a7cffb4b294f79248de0271 GIT binary patch literal 7971 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 T$N`lq{#pEkG~|BBn1}r@Ed)DY literal 0 HcmV?d00001 diff --git a/apps/web/src/assets/graphql.png b/apps/web/src/assets/graphql.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f434d5053d1394ca5e369bbfb337e65918add1 GIT binary patch literal 3709 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 bz=z;Jaf$v^$++RF00000NkvXXu0mjf9FIWD literal 0 HcmV?d00001 diff --git a/apps/web/src/assets/mantine.svg b/apps/web/src/assets/mantine.svg new file mode 100644 index 0000000..09e4c94 --- /dev/null +++ b/apps/web/src/assets/mantine.svg @@ -0,0 +1,8 @@ + + + Mantine + + + + + diff --git a/apps/web/src/assets/nestjs.svg b/apps/web/src/assets/nestjs.svg new file mode 100644 index 0000000..39bcd04 --- /dev/null +++ b/apps/web/src/assets/nestjs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/src/assets/prisma.svg b/apps/web/src/assets/prisma.svg new file mode 100644 index 0000000..048a467 --- /dev/null +++ b/apps/web/src/assets/prisma.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/web/src/assets/react.svg b/apps/web/src/assets/react.svg new file mode 100644 index 0000000..3a94b27 --- /dev/null +++ b/apps/web/src/assets/react.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/apps/web/src/index.html b/apps/web/src/index.html index dff7a09..625ff78 100644 --- a/apps/web/src/index.html +++ b/apps/web/src/index.html @@ -7,6 +7,9 @@ + + +
diff --git a/libs/api/metadata/data-access/src/lib/generators/generate-business-visa-image.tsx b/libs/api/metadata/data-access/src/lib/generators/generate-business-visa-image.tsx index ccef299..2a1e910 100644 --- a/libs/api/metadata/data-access/src/lib/generators/generate-business-visa-image.tsx +++ b/libs/api/metadata/data-access/src/lib/generators/generate-business-visa-image.tsx @@ -115,10 +115,6 @@ function TemplateRender({ const base64PoweredBy = Buffer.from(poweredBy).toString('base64') const base64Icon = Buffer.from(icon).toString('base64') const base64Logo = Buffer.from(logo).toString('base64') - const base64Discord = Buffer.from(brands.discord).toString('base64') - const base64Github = Buffer.from(brands.github).toString('base64') - const base64Google = Buffer.from(brands.google).toString('base64') - const base64X = Buffer.from(brands.x).toString('base64') const gray = '#383838' const brand = '#8743FF' diff --git a/libs/api/preset/data-access/src/lib/api-preset-data-user.service.ts b/libs/api/preset/data-access/src/lib/api-preset-data-user.service.ts index 0cf59ce..e1e6f3c 100644 --- a/libs/api/preset/data-access/src/lib/api-preset-data-user.service.ts +++ b/libs/api/preset/data-access/src/lib/api-preset-data-user.service.ts @@ -58,8 +58,7 @@ export class ApiPresetDataUserService { throw new Error('Minter does not belong to this community') } - // TODO: delete minter - return false + return this.minter.deleteMinter({ minter, communitySlug }) } async addMinterAuthority(userId: string, account: string, authority: string, communitySlug: string): Promise { diff --git a/libs/api/preset/data-access/src/lib/api-preset-minter.service.ts b/libs/api/preset/data-access/src/lib/api-preset-minter.service.ts index e71070c..e0f40cb 100644 --- a/libs/api/preset/data-access/src/lib/api-preset-minter.service.ts +++ b/libs/api/preset/data-access/src/lib/api-preset-minter.service.ts @@ -29,7 +29,6 @@ import { VersionedTransaction, } from '@solana/web3.js' import { ApiCoreService } from '@tokengator/api-core-data-access' -import { TokenGatorActivityEntryInput } from './dto/token-gator-activity-entry.input' import { ApiSolanaService } from '@tokengator/api-solana-data-access' import { getActivityPda, @@ -50,6 +49,7 @@ import { LRUCache } from 'lru-cache' import { ApiPresetDataService } from './api-preset-data.service' import { PresetUserMintFromMinter } from './dto/preset-user-mint-from-minter' import { PresetUserMintFromPreset } from './dto/preset-user-mint-from-preset' +import { TokenGatorActivityEntryInput } from './dto/token-gator-activity-entry.input' import { TokenGatorActivity } from './entity/token-gator-activity.entity' import { TokenGatorMinter } from './entity/token-gator-minter.entity' import { formatTokenGatorMinter } from './helpers/format-token-gator-minter' @@ -889,6 +889,30 @@ export class ApiPresetMinterService { .account.tokenGroupMember.all([{ memcmp: { offset: 32 + 8, bytes: account.toBase58() } }]) .then((res) => res.sort((a, b) => a.account.memberNumber - b.account.memberNumber)) } + + async deleteMinter({ communitySlug, minter }: { communitySlug: string; minter: TokenGatorMinter }) { + this.logger.verbose(`Deleting minter: ${minter.publicKey} from community: ${communitySlug}`) + const authority = await this.getKeypairFromCommunity(communitySlug) + const feePayer = this.feePayer + const minterProgram = this.getProgramTokenMinter(this.solana.getAnchorProvider(feePayer)) + + const signature = await minterProgram.methods + .removeMinter() + .accounts({ + minter: minter.publicKey, + mint: minter.minterConfig.mint, + feePayer: feePayer.publicKey, + authority: authority.publicKey, + tokenProgram: TOKEN_2022_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }) + .signers([authority, feePayer]) + .rpc({ commitment: 'confirmed', skipPreflight: true }) + + this.logger.verbose(`Signature: ${signature}`) + return signature + } } function getPresetConfig({ communitySlug, url, preset }: { communitySlug: string; url: string; preset: Preset }) { diff --git a/libs/sdk/src/generated/graphql-sdk.ts b/libs/sdk/src/generated/graphql-sdk.ts index ae7d4d5..c61a3ab 100644 --- a/libs/sdk/src/generated/graphql-sdk.ts +++ b/libs/sdk/src/generated/graphql-sdk.ts @@ -3615,6 +3615,13 @@ export type UserRemoveMinterAuthorityMutationVariables = Exact<{ export type UserRemoveMinterAuthorityMutation = { __typename?: 'Mutation'; removed?: string | null } +export type UserDeleteMinterMutationVariables = Exact<{ + account: Scalars['String']['input'] + communitySlug: Scalars['String']['input'] +}> + +export type UserDeleteMinterMutation = { __typename?: 'Mutation'; deleted?: boolean | null } + export type PriceDetailsFragment = { __typename?: 'Price' createdAt?: Date | null @@ -5205,6 +5212,11 @@ export const UserRemoveMinterAuthorityDocument = gql` removed: userRemoveMinterAuthority(account: $account, authority: $authority, communitySlug: $communitySlug) } ` +export const UserDeleteMinterDocument = gql` + mutation userDeleteMinter($account: String!, $communitySlug: String!) { + deleted: userDeleteMinter(account: $account, communitySlug: $communitySlug) + } +` export const AdminFindManyPriceDocument = gql` query adminFindManyPrice($input: PriceAdminFindManyInput!) { items: adminFindManyPrice(input: $input) { @@ -5521,6 +5533,7 @@ const UserCreateMintFromPresetDocumentString = print(UserCreateMintFromPresetDoc const UserCreateMintFromMinterDocumentString = print(UserCreateMintFromMinterDocument) const UserAddMinterAuthorityDocumentString = print(UserAddMinterAuthorityDocument) const UserRemoveMinterAuthorityDocumentString = print(UserRemoveMinterAuthorityDocument) +const UserDeleteMinterDocumentString = print(UserDeleteMinterDocument) const AdminFindManyPriceDocumentString = print(AdminFindManyPriceDocument) const AdminFindOnePriceDocumentString = print(AdminFindOnePriceDocument) const AdminCreatePriceDocumentString = print(AdminCreatePriceDocument) @@ -6998,6 +7011,27 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = variables, ) }, + userDeleteMinter( + variables: UserDeleteMinterMutationVariables, + requestHeaders?: GraphQLClientRequestHeaders, + ): Promise<{ + data: UserDeleteMinterMutation + errors?: GraphQLError[] + extensions?: any + headers: Headers + status: number + }> { + return withWrapper( + (wrappedRequestHeaders) => + client.rawRequest(UserDeleteMinterDocumentString, variables, { + ...requestHeaders, + ...wrappedRequestHeaders, + }), + 'userDeleteMinter', + 'mutation', + variables, + ) + }, adminFindManyPrice( variables: AdminFindManyPriceQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, diff --git a/libs/sdk/src/graphql/feature-preset.graphql b/libs/sdk/src/graphql/feature-preset.graphql index 70db2cb..ecd19c6 100644 --- a/libs/sdk/src/graphql/feature-preset.graphql +++ b/libs/sdk/src/graphql/feature-preset.graphql @@ -96,3 +96,7 @@ mutation userAddMinterAuthority($account: String!, $authority: String!, $communi mutation userRemoveMinterAuthority($account: String!, $authority: String!, $communitySlug: String!) { removed: userRemoveMinterAuthority(account: $account, authority: $authority, communitySlug: $communitySlug) } + +mutation userDeleteMinter($account: String!, $communitySlug: String!) { + deleted: userDeleteMinter(account: $account, communitySlug: $communitySlug) +} diff --git a/libs/web/asset/feature/src/lib/user-asset-detail-feature.tsx b/libs/web/asset/feature/src/lib/user-asset-detail-feature.tsx index e6313e6..b37ce8f 100644 --- a/libs/web/asset/feature/src/lib/user-asset-detail-feature.tsx +++ b/libs/web/asset/feature/src/lib/user-asset-detail-feature.tsx @@ -69,9 +69,10 @@ export function UserAssetDetailFeature() { } function UserAssetActivities({ asset }: { asset: TokenGatorAsset }) { + const types = asset.activities || [] return ( - - {asset.activities?.map((type) => ( + + {types?.map((type) => ( @@ -160,9 +161,9 @@ export function CreateEventFrom({ }) { const form = useForm({ initialValues: { - message: `Event ${Date.now()}`, - url: 'https://example.com', - points: 1, + message: '', + url: '', + points: 0, }, }) diff --git a/libs/web/asset/ui/src/lib/asset-activity-ui-entry-list.tsx b/libs/web/asset/ui/src/lib/asset-activity-ui-entry-list.tsx index 44bf1fe..805ec92 100644 --- a/libs/web/asset/ui/src/lib/asset-activity-ui-entry-list.tsx +++ b/libs/web/asset/ui/src/lib/asset-activity-ui-entry-list.tsx @@ -1,5 +1,5 @@ import { Text, Timeline } from '@mantine/core' -import { UiTime } from '@pubkey-ui/core' +import { UiAnchor, UiTime } from '@pubkey-ui/core' import { IconCheck } from '@tabler/icons-react' import { TokenGatorActivity, TokenGatorActivityEntry } from '@tokengator/sdk' import { AssetActivityUiPoints } from './asset-activity-ui-points' @@ -14,7 +14,15 @@ export function AssetActivityUiEntryList({ return ( {entries.reverse().map((entry) => ( - } title={entry.message}> + } + title={ + + {entry.message} + + } + > {(entry?.points ?? 0) > 0 ? ( Earned diff --git a/libs/web/community/feature/src/lib/user-community-detail-minter-detail-tab.tsx b/libs/web/community/feature/src/lib/user-community-detail-minter-detail-tab.tsx index 0120432..2c74cf7 100644 --- a/libs/web/community/feature/src/lib/user-community-detail-minter-detail-tab.tsx +++ b/libs/web/community/feature/src/lib/user-community-detail-minter-detail-tab.tsx @@ -38,7 +38,13 @@ export function UserCommunityDetailMinterDetailTab({ community }: { community: C mutation.mutateAsync({ username }).then(() => queryAssets.refetch()) }} /> - {items?.length ? : } + {queryAssets.isLoading ? ( + + ) : items?.length ? ( + + ) : ( + + )} ), }, diff --git a/libs/web/community/feature/src/lib/user-community-detail-minter-list-tab.tsx b/libs/web/community/feature/src/lib/user-community-detail-minter-list-tab.tsx index c624320..6a23764 100644 --- a/libs/web/community/feature/src/lib/user-community-detail-minter-list-tab.tsx +++ b/libs/web/community/feature/src/lib/user-community-detail-minter-list-tab.tsx @@ -1,14 +1,25 @@ import { Button, Group, Select } from '@mantine/core' import { modals } from '@mantine/modals' import { UiDebug, UiDebugModal, UiInfo, UiLoader, UiStack } from '@pubkey-ui/core' +import { useMutation } from '@tanstack/react-query' import { Community, Preset, TokenGatorMinter } from '@tokengator/sdk' import { useUserFindOneCommunity, useUserGetMintersByCommunity } from '@tokengator/web-community-data-access' import { MinterUiList } from '@tokengator/web-community-ui' +import { useSdk } from '@tokengator/web-core-data-access' import { useUserFindManyPreset } from '@tokengator/web-preset-data-access' import { useState } from 'react' +function useUserDeleteMinter({ communitySlug }: { communitySlug: string }) { + const sdk = useSdk() + + return useMutation({ + mutationFn: (account: string) => sdk.userDeleteMinter({ account, communitySlug }).then((res) => res.data.deleted), + }) +} + export function UserCommunityDetailMinterListTab({ community }: { community: Community }) { const { createMinter } = useUserFindOneCommunity({ slug: community.slug }) + const mutationDelete = useUserDeleteMinter({ communitySlug: community.slug }) const query = useUserGetMintersByCommunity({ slug: community.slug }) const { items: presets } = useUserFindManyPreset() const items: TokenGatorMinter[] = query.data ?? [] @@ -20,7 +31,12 @@ export function UserCommunityDetailMinterListTab({ community }: { community: Com {query.isLoading ? ( ) : items.length ? ( - + { + mutationDelete.mutateAsync(item).then(() => query.refetch()) + }} + /> ) : ( )} diff --git a/libs/web/community/ui/src/lib/minter-ui-list.tsx b/libs/web/community/ui/src/lib/minter-ui-list.tsx index 5727213..68aa43a 100644 --- a/libs/web/community/ui/src/lib/minter-ui-list.tsx +++ b/libs/web/community/ui/src/lib/minter-ui-list.tsx @@ -1,14 +1,38 @@ -import { UiStack } from '@pubkey-ui/core' +import { ActionIcon, Group, SimpleGrid, Text, Tooltip } from '@mantine/core' +import { IconTrash } from '@tabler/icons-react' import { TokenGatorMinter } from '@tokengator/sdk' import { MinterUiCard } from './minter-ui-card' -export function MinterUiList({ items }: { items: TokenGatorMinter[] }) { +export function MinterUiList({ + items, + deleteMinter, +}: { + items: TokenGatorMinter[] + deleteMinter: (account: string) => Promise +}) { return ( - + {items.map((item, index) => ( - + + {item.description} + + + + { + deleteMinter(item.publicKey) + }} + > + + + + + ))} - + ) } diff --git a/libs/web/home/feature/src/lib/home-routes.tsx b/libs/web/home/feature/src/lib/home-routes.tsx index d805316..a842a76 100644 --- a/libs/web/home/feature/src/lib/home-routes.tsx +++ b/libs/web/home/feature/src/lib/home-routes.tsx @@ -2,9 +2,8 @@ import { lazy } from 'react' import { RouteObject } from 'react-router-dom' const LazyHomeFeature = lazy(() => import('./pages/home-page')) -const LazyAboutPage = lazy(() => import('./pages/about-page')) export const webHomeRoutes: RouteObject[] = [ + // More routes can be added here { path: '/home', element: }, - { path: '/about', element: }, ] diff --git a/libs/web/home/feature/src/lib/pages/about-page.tsx b/libs/web/home/feature/src/lib/pages/about-page.tsx deleted file mode 100644 index c0aafc4..0000000 --- a/libs/web/home/feature/src/lib/pages/about-page.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Button, Container, Group, Text, Title } from '@mantine/core' -import { UiStack } from '@pubkey-ui/core' -import { IconHome } from '@tabler/icons-react' -import { Link } from 'react-router-dom' - -export default function AboutPage() { - return ( - - - About TokenGator. - This is an empty about page. - - - - - - ) -} diff --git a/libs/web/home/feature/src/lib/pages/home-page.tsx b/libs/web/home/feature/src/lib/pages/home-page.tsx index a5bee25..c9982c0 100644 --- a/libs/web/home/feature/src/lib/pages/home-page.tsx +++ b/libs/web/home/feature/src/lib/pages/home-page.tsx @@ -1,34 +1,47 @@ -import { Button, Container, Group, Text, Title } from '@mantine/core' -import { UiStack } from '@pubkey-ui/core' -import { IconBrandGithub, IconRocket } from '@tabler/icons-react' -import { Link } from 'react-router-dom' +import { Box, useMantineTheme } from '@mantine/core' +import { UiStack, useUiColorScheme } from '@pubkey-ui/core' +import { ReactNode } from 'react' +import { HomepageFeatures } from '../ui/homepage-features/homepage-features' +import { HomepageFooter } from '../ui/homepage-footer/homepage-footer' +import { HomepageHeader } from '../ui/homepage-header/homepage-header' +import { HomepageHero } from '../ui/homepage-hero/homepage-hero' +import { HomepagePricing } from '../ui/homepage-pricing/homepage-pricing' +import { HomepageTechnology } from '../ui/homepage-technology/homepage-technology' +import { HomepageUseCases } from '../ui/homepage-use-cases/homepage-use-cases' export default function HomePage() { + const items: { id: string; element: ReactNode }[] = [ + { id: 'homepage', element: }, + { id: 'features', element: }, + { id: 'pricing', element: }, + { id: 'use-cases', element: }, + { id: 'technology', element: }, + ] + return ( - - - Welcome to TokenGator. + + + {items.map(({ id, element }, index) => ( + + {element} + + ))} + + + ) +} - This is the TokenGator project website. - - - +function HomepageSection({ children, id, index }: { children: ReactNode; id: string; index: number }) { + const { colors } = useMantineTheme() + const { colorScheme } = useUiColorScheme() + const isDark = colorScheme === 'dark' + const isOdd = index % 2 === 0 - - - - + const bg = isDark ? colors.dark[isOdd ? 8 : 9] : isOdd ? colors.gray[0] : colors.gray[1] + + return ( + + {children} + ) } diff --git a/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.module.css b/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.module.css new file mode 100644 index 0000000..62f15a6 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.module.css @@ -0,0 +1,24 @@ +.wrapper { + padding-top: calc(var(--mantine-spacing-xl) * 4); + padding-bottom: calc(var(--mantine-spacing-xl) * 4); +} + +.title { + font-family: 'Baloo Bhai 2', var(--mantine-font-family); + font-weight: 900; + margin-bottom: var(--mantine-spacing-md); + text-align: center; + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(28px); + text-align: left; + } +} + +.description { + text-align: center; + + @media (max-width: $mantine-breakpoint-sm) { + text-align: left; + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.tsx b/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.tsx new file mode 100644 index 0000000..73adc75 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-features/homepage-features.tsx @@ -0,0 +1,99 @@ +import { Container, rem, SimpleGrid, Text, ThemeIcon, Title } from '@mantine/core' +import { IconGauge, IconMoneybag, IconPhoto, IconPuzzle, IconSourceCode, IconUsersGroup } from '@tabler/icons-react' +import { ComponentType, CSSProperties, ReactNode } from 'react' +import classes from './homepage-features.module.css' + +export const FEATURES: FeatureProps[] = [ + { + icon: IconGauge, + title: 'Holistic Digital Asset Management', + description: + 'TokenGator simplifies the creation, issuance, and management of digital assets on Solana, offering a comprehensive solution for DAOs and Network States.', + }, + { + icon: IconPhoto, + title: 'Dynamic NFT Collections', + description: + 'Leverage TokenGator to craft dynamic NFT collections that evolve with your community. Utilize Solana Token Extensions for enhanced functionality and engagement.', + }, + { + icon: IconUsersGroup, + title: 'Community-Centric Features', + description: + 'Designed with communities in mind, TokenGator supports diverse document management needs, from business visas to event access, tailored to your community’s identity.', + }, + { + icon: IconMoneybag, + title: 'Transparent and Customizable Pricing', + description: + 'Choose from flexible pricing tiers designed to fit the scale and scope of your project, ensuring you only pay for what you need.', + }, + { + icon: IconPuzzle, + title: 'Easy Integration', + description: + 'Incorporate TokenGator’s Anchor Program, SDK, and API into your existing systems for seamless integration and maximal composability within the Solana ecosystem.', + }, + { + icon: IconSourceCode, + title: 'Open Source and MIT Licensed', + description: + 'TokenGator is committed to transparency and flexibility, offering its powerful platform under the MIT License, enabling free, open-source access and customization to meet your unique needs.', + }, +] + +interface FeatureProps { + icon: ComponentType<{ color?: string; size?: number; style?: CSSProperties; stroke?: number }> + title: ReactNode + description: ReactNode +} + +export function Feature({ icon: Icon, title, description }: FeatureProps) { + return ( +
+ + + + + {title} + + + {description} + +
+ ) +} + +export function HomepageFeatures() { + const features = FEATURES.map((feature, index) => ) + + return ( + + + Unleash Your Community's Potential with TokenGator + + + + + Explore a suite of robust features designed to revolutionize digital asset management on Solana. From dynamic + NFT collections to open-source flexibility, TokenGator is your key to engaging and expanding your community. + + + + + {features} + + + ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.module.css b/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.module.css new file mode 100644 index 0000000..4d0717c --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.module.css @@ -0,0 +1,89 @@ +.footer { + margin-top: rem(120px); + padding-top: calc(var(--mantine-spacing-xl) * 2); + padding-bottom: calc(var(--mantine-spacing-xl) * 2); + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9)); + border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6)); +} + +.logo { + max-width: rem(210px); + + @media (max-width: $mantine-breakpoint-sm) { + display: flex; + flex-direction: column; + align-items: center; + } +} + +.description { + margin-top: rem(5px); + + @media (max-width: $mantine-breakpoint-sm) { + margin-top: var(--mantine-spacing-xs); + text-align: center; + } +} + +.inner { + display: flex; + justify-content: space-between; + + @media (max-width: $mantine-breakpoint-sm) { + flex-direction: column; + align-items: center; + } +} + +.groups { + display: flex; + flex-wrap: wrap; + + @media (max-width: $mantine-breakpoint-sm) { + display: none; + } +} + +.wrapper { + width: rem(160px); +} + +.link { + display: block; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-sm); + padding-top: rem(3px); + padding-bottom: rem(3px); + + &:hover { + text-decoration: underline; + } +} + +.title { + font-size: var(--mantine-font-size-lg); + font-weight: 700; + font-family: 'Baloo Bhai 2', var(--mantine-font-family); + margin-bottom: calc(var(--mantine-spacing-xs) / 2); + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); +} + +.afterFooter { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: var(--mantine-spacing-xl); + padding-top: var(--mantine-spacing-xl); + padding-bottom: var(--mantine-spacing-xl); + border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4)); + + @media (max-width: $mantine-breakpoint-sm) { + flex-direction: column; + } +} + +.social { + @media (max-width: $mantine-breakpoint-sm) { + margin-top: var(--mantine-spacing-xs); + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.tsx b/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.tsx new file mode 100644 index 0000000..bcfa6b1 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-footer/homepage-footer.tsx @@ -0,0 +1,106 @@ +import { ActionIcon, Anchor, Container, Group, rem, Stack, Text, Tooltip } from '@mantine/core' +import { UiAnchor, UiLogoType } from '@pubkey-ui/core' +import { IconBrandDiscord, IconBrandGithub, IconBrandX } from '@tabler/icons-react' +import { AppLogoType } from '@tokengator/web-core-ui' +import classes from './homepage-footer.module.css' + +const data = [ + { + title: 'About', + links: [ + { label: 'Home', link: '/home#homepage' }, + { label: 'Features', link: '/home#features' }, + { label: 'Pricing', link: '/home#pricing' }, + { label: 'Team', link: '/home#team' }, + ], + }, + { + title: 'Project', + links: [ + { label: 'Platform', link: 'https://github.com/pubkeyapp/tokengator' }, + { label: 'Program Library', link: 'https://github.com/pubkeyapp/tokengator-program-library' }, + { + label: 'Deployed Program', + link: 'https://solana.fm/address/GAToRDEEZmbXSe7ECcChQ1TsZCQXDBCtVhSd1Ypas9h6?cluster=devnet-alpha', + }, + ], + }, + { + title: 'Community', + links: [ + { label: 'Join Discord', link: 'https://discord.gg/XxuZQeDPNf' }, + { label: 'Follow on X', link: 'https://x.com/TokenGator' }, + { label: 'Star on GitHub', link: 'https://github.com/pubkeyapp/tokengator' }, + ], + }, +] + +export function HomepageFooter() { + const groups = data.map((group) => { + const links = group.links.map((link, index) => ( + + {link.label} + + )) + + return ( +
+ {group.title} + {links} +
+ ) + }) + + return ( +
+ +
+ + + The Easiest Way to Manage Dynamic NFT Collections on Solana. + +
+
{groups}
+
+ + + + + TokenGator is a{' '} + + PubKey + {' '} + Project built for the{' '} + + Renaissance Hackathon. + + + + + + + + + + + + + + + + + + + + + + +
+ ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.module.css b/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.module.css new file mode 100644 index 0000000..22fff99 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.module.css @@ -0,0 +1,32 @@ +.header { + height: rem(56px); + background-color: light-dark(var(--mantine-color-body), var(--mantine-color-dark-8)) !important; + border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-6)); +} + +.inner { + height: rem(56px); + display: flex; + justify-content: space-between; + align-items: center; +} + +.link { + display: block; + line-height: 1; + padding: rem(8px) rem(12px); + border-radius: var(--mantine-radius-sm); + text-decoration: none; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); + font-size: var(--mantine-font-size-sm); + font-weight: 500; + + @mixin hover { + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } + + [data-mantine-color-scheme] &[data-active] { + background-color: var(--mantine-color-brand-filled); + color: var(--mantine-color-white); + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.tsx b/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.tsx new file mode 100644 index 0000000..6cd80f2 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-header/homepage-header.tsx @@ -0,0 +1,57 @@ +import { Burger, Button, Container, Group } from '@mantine/core' +import { useDisclosure } from '@mantine/hooks' +import { IconRocket } from '@tabler/icons-react' +import { AppLogoType } from '@tokengator/web-core-ui' + +import { useState } from 'react' +import { Link } from 'react-router-dom' +import classes from './homepage-header.module.css' + +const links = [ + { link: '/home#home', label: 'Home' }, + { link: '/home#features', label: 'Features' }, + { link: '/home#pricing', label: 'Pricing' }, + { link: '/home#team', label: 'Team' }, +] + +export function HomepageHeader() { + const [opened, { toggle }] = useDisclosure(false) + const [active, setActive] = useState(links[0].link) + + const items = links.map((link, index) => ( + { + setActive(link.link) + }} + > + {link.label} + + )) + + return ( +
+ + + + {items} + + + + + +
+ ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.module.css b/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.module.css new file mode 100644 index 0000000..c72df93 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.module.css @@ -0,0 +1,61 @@ +.wrapper { + position: relative; + box-sizing: border-box; + /*background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));*/ +} + +.inner { + position: relative; + padding-top: rem(60px); + padding-bottom: rem(60px); + + @media (max-width: $mantine-breakpoint-sm) { + padding-bottom: rem(80px); + padding-top: rem(80px); + } +} + +.title { + font-family: 'Baloo Bhai 2', var(--mantine-font-family); + font-size: rem(62px); + font-weight: 900; + line-height: 1.1; + margin: 0; + padding: 0; + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(42px); + line-height: 1.2; + } +} + +.description { + margin-top: var(--mantine-spacing-xl); + font-size: rem(24px); + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(18px); + } +} + +.controls { + margin-top: calc(var(--mantine-spacing-xl) * 2); + + @media (max-width: $mantine-breakpoint-sm) { + margin-top: var(--mantine-spacing-xl); + } +} + +.control { + height: rem(54px); + padding-left: rem(38px); + padding-right: rem(38px); + + @media (max-width: $mantine-breakpoint-sm) { + height: rem(54px); + padding-left: rem(18px); + padding-right: rem(18px); + flex: 1; + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.tsx b/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.tsx new file mode 100644 index 0000000..585d558 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-hero/homepage-hero.tsx @@ -0,0 +1,53 @@ +import { Button, Container, Group, Text } from '@mantine/core' +import { IconBrandGithub, IconRocket } from '@tabler/icons-react' +import { solanaGradient } from '@tokengator/sdk' +import { Link } from 'react-router-dom' +import classes from './homepage-hero.module.css' + +export function HomepageHero() { + return ( +
+ +

+ The Easiest Way to Manage Dynamic NFT Collections on{' '} + + Solana + + .{' '} +

+ + + Gone are the days of manual scripts and tooling to manage your NFT collections. TokenGator is a powerful + platform that allows you to create, manage, and distribute dynamic NFT collections with ease. + + + + + + + +
+
+ ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-pricing/homepage-pricing.tsx b/libs/web/home/feature/src/lib/ui/homepage-pricing/homepage-pricing.tsx new file mode 100644 index 0000000..115ee34 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-pricing/homepage-pricing.tsx @@ -0,0 +1,151 @@ +import { Box, Button, Flex, Group, Stack, Text, Title, useMantineTheme } from '@mantine/core' +import { useUiColorScheme } from '@pubkey-ui/core' +import { Link } from 'react-router-dom' + +export function HomepagePricing() { + const theme = useMantineTheme() + const { colorScheme } = useUiColorScheme() + const isDark = colorScheme === 'dark' + + const prices: { + title: string + price: string + features: string[] + }[] = [ + { + title: 'Self-hosted', + price: 'Free', + features: ['Unlimited Managed NFTs', 'Open-source MIT License', 'Community Support', 'Bring your own Server'], + }, + { + title: 'Monthly', + price: '49.99', + features: ['100 Managed NFTs', 'Custom Branding', 'Custom Integrations', 'Chat Support'], + }, + { + title: 'Yearly', + price: '499.99', + features: ['100 Managed NFTs', 'Custom Branding', 'Custom Integrations', 'Priority Support'], + }, + ] + + return ( + + + + + TokenGator Pricing + + + + + + {prices.map((price, index) => { + const isMiddle = index === 1 + const isLast = index === prices.length - 1 + + return ( + + + + {price.title} + + + <Text fz={'2rem'}>$</Text> + {price.price} + + + {price.features.map((feature, index) => ( + + {feature} + + ))} + + + + + ) + })} + + + + + ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.module.css b/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.module.css new file mode 100644 index 0000000..62f15a6 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.module.css @@ -0,0 +1,24 @@ +.wrapper { + padding-top: calc(var(--mantine-spacing-xl) * 4); + padding-bottom: calc(var(--mantine-spacing-xl) * 4); +} + +.title { + font-family: 'Baloo Bhai 2', var(--mantine-font-family); + font-weight: 900; + margin-bottom: var(--mantine-spacing-md); + text-align: center; + + @media (max-width: $mantine-breakpoint-sm) { + font-size: rem(28px); + text-align: left; + } +} + +.description { + text-align: center; + + @media (max-width: $mantine-breakpoint-sm) { + text-align: left; + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.tsx b/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.tsx new file mode 100644 index 0000000..0ccedcf --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-technology/homepage-technology.tsx @@ -0,0 +1,101 @@ +import { Anchor, Avatar, Container, SimpleGrid, Text, Title } from '@mantine/core' +import { ReactNode } from 'react' +import classes from './homepage-technology.module.css' + +export const FEATURES: FeatureProps[] = [ + { + logo: '/assets/anchor.png', + title: 'Anchor: Solana Program Framework', + description: + "Anchor serves as the foundation for TokenGator's Solana programs, enabling rapid and secure development of smart contracts. This framework ensures that our digital asset management solutions are both efficient and reliable, leveraging Solana's blockchain capabilities to the fullest.", + }, + { + logo: '/assets/nestjs.svg', + title: 'Nest.js: Scalable API Development', + description: + "At the heart of TokenGator's backend is Nest.js, a powerful framework for building efficient and scalable server-side applications. Nest.js allows us to create a highly performant API layer that seamlessly integrates with our blockchain infrastructure, ensuring fast and secure data handling.", + }, + { + logo: '/assets/prisma.svg', + title: 'Prisma: Next-Generation ORM', + description: + 'Prisma revolutionizes our database management, offering a next-generation ORM for Node.js and TypeScript. It simplifies database access, schema management, and migrations, enabling TokenGator to handle complex data models with ease and efficiency.', + }, + { + logo: '/assets/graphql.png', + title: 'GraphQL: Flexible Data Queries', + description: + 'TokenGator utilizes GraphQL to provide a flexible and efficient API for data queries and mutations. This allows users and developers to retrieve exactly the data they need from our platform, reducing bandwidth and improving the user experience.', + }, + { + logo: '/assets/react.svg', + title: 'React: Interactive UIs', + description: + "Our user interfaces are built with React, ensuring that interacting with TokenGator is smooth, responsive, and engaging. React's component-based architecture allows us to create dynamic and reusable UI elements, enhancing the overall user journey on our platform.", + }, + { + logo: '/assets/mantine.svg', + title: 'Mantine: Modern React UI Framework', + description: + "Mantine enhances TokenGator's user interface with its comprehensive suite of fully customizable React components. Focused on usability and accessibility, Mantine helps us deliver a beautiful and user-friendly experience across all aspects of our platform.", + }, +] + +interface FeatureProps { + logo?: string + title: ReactNode + description: ReactNode +} + +export function Feature({ title, logo, description }: FeatureProps) { + return ( +
+ + + {title} + + + {description} + +
+ ) +} + +export function HomepageTechnology() { + const features = FEATURES.map((feature, index) => ) + + return ( + + + TokenGator Technology + + + + + On the base of TokenGator's digital asset management platform lies a powerful technology. As a starting point, + we used{' '} + + PubKey Stack + {' '} + which is a full-stack TypeScript template for building web applications with React, NestJS, Prisma, and + GraphQL. + + + + + {features} + + + ) +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.module.css b/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.module.css new file mode 100644 index 0000000..0f25df4 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.module.css @@ -0,0 +1,47 @@ +.wrapper { + padding-top: calc(var(--mantine-spacing-xl) * 2); + min-height: rem(500px); + position: relative; +} + +.item { + background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7)); + border-bottom: 0; + border-radius: var(--mantine-radius-md); + box-shadow: var(--mantine-shadow-lg); + overflow: hidden; +} + +.control { + font-size: var(--mantine-font-size-lg); + padding: var(--mantine-spacing-lg) var(--mantine-spacing-xl); + + @mixin hover { + background-color: transparent; + } +} + +.content { + padding-left: var(--mantine-spacing-xl); + line-height: 1.6; +} + +.icon { + margin-left: var(--mantine-spacing-md); +} + +.itemOpened { + & .icon { + transform: rotate(45deg); + } +} + +.button { + display: block; + margin-top: var(--mantine-spacing-md); + + @media (max-width: $mantine-breakpoint-sm) { + display: block; + width: 100%; + } +} diff --git a/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.tsx b/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.tsx new file mode 100644 index 0000000..cec8c35 --- /dev/null +++ b/libs/web/home/feature/src/lib/ui/homepage-use-cases/homepage-use-cases.tsx @@ -0,0 +1,60 @@ +import { Accordion, Container, rem, ThemeIcon, Title } from '@mantine/core' +import { IconPlus } from '@tabler/icons-react' +import classes from './homepage-use-cases.module.css' + +export function HomepageUseCases() { + const items: { + title: string + content: string + }[] = [ + { + title: "Dean's List DAO Business Visa", + content: `Dean's List DAO to issue Business Visas, facilitating members' ability to earn within the community. This innovative use case surpasses traditional limitations by utilizing dynamic NFTs, ensuring a seamless and enhanced user experience for both issuers and holders.`, + }, + { + title: 'IslandDAOx Event NFT', + content: `Streamline Event Management and Access: For communities like Island DAO, TokenGator offers a solution to control access to facilities and events through sol-bound NFTs. These NFTs can include expiration dates and membership levels, such as staff, sponsor, visitor, and guest, making event access management straightforward and secure.`, + }, + { + title: 'Solana Mobile Chapter 2', + content: `Innovate with Solana Mobile Integration: Although briefly mentioned, this use case hints at the potential for TokenGator to integrate with Solana Mobile efforts, potentially facilitating mobile-friendly digital asset management and engagement, opening new avenues for community interaction and asset utilization.`, + }, + ] + return ( +
+ + + TokenGator Use Cases + + + + + + } + > + {items.map((item) => ( + + {item.title} + {item.content} + + ))} + + +
+ ) +}