diff --git a/functions/graphql/nexus-typegen.ts b/functions/graphql/nexus-typegen.ts index c626bf8..d061a36 100644 --- a/functions/graphql/nexus-typegen.ts +++ b/functions/graphql/nexus-typegen.ts @@ -85,10 +85,10 @@ export interface NexusGenObjects { payment_request: string; // String! } DonationsStats: { // root type - applications: number; // Int! - donations: number; // Int! - prizes: number; // Int! - touranments: number; // Int! + applications: string; // String! + donations: string; // String! + prizes: string; // String! + touranments: string; // String! } Hackathon: { // root type cover_image: string; // String! @@ -232,10 +232,10 @@ export interface NexusGenFieldTypes { payment_request: string; // String! } DonationsStats: { // field return type - applications: number; // Int! - donations: number; // Int! - prizes: number; // Int! - touranments: number; // Int! + applications: string; // String! + donations: string; // String! + prizes: string; // String! + touranments: string; // String! } Hackathon: { // field return type cover_image: string; // String! @@ -289,7 +289,7 @@ export interface NexusGenFieldTypes { allTopics: NexusGenRootTypes['Topic'][]; // [Topic!]! getAllHackathons: NexusGenRootTypes['Hackathon'][]; // [Hackathon!]! getCategory: NexusGenRootTypes['Category']; // Category! - getDonationsStats: NexusGenRootTypes['DonationsStats'][]; // [DonationsStats!]! + getDonationsStats: NexusGenRootTypes['DonationsStats']; // DonationsStats! getFeed: NexusGenRootTypes['Post'][]; // [Post!]! getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails! getPostById: NexusGenRootTypes['Post']; // Post! @@ -411,10 +411,10 @@ export interface NexusGenFieldTypeNames { payment_request: 'String' } DonationsStats: { // field return type name - applications: 'Int' - donations: 'Int' - prizes: 'Int' - touranments: 'Int' + applications: 'String' + donations: 'String' + prizes: 'String' + touranments: 'String' } Hackathon: { // field return type name cover_image: 'String' diff --git a/functions/graphql/schema.graphql b/functions/graphql/schema.graphql index 07ff7dc..c11e1be 100644 --- a/functions/graphql/schema.graphql +++ b/functions/graphql/schema.graphql @@ -58,10 +58,10 @@ type Donation { } type DonationsStats { - applications: Int! - donations: Int! - prizes: Int! - touranments: Int! + applications: String! + donations: String! + prizes: String! + touranments: String! } type Hackathon { @@ -138,7 +138,7 @@ type Query { allTopics: [Topic!]! getAllHackathons(sortBy: String, topic: Int): [Hackathon!]! getCategory(id: Int!): Category! - getDonationsStats: [DonationsStats!]! + getDonationsStats: DonationsStats! getFeed(skip: Int = 0, sortBy: String = "all", take: Int = 10, topic: Int = 0): [Post!]! getLnurlDetailsForProject(project_id: Int!): LnurlDetails! getPostById(id: Int!, type: POST_TYPE!): Post! diff --git a/functions/graphql/types/donation.js b/functions/graphql/types/donation.js index fb9d57d..ed7e327 100644 --- a/functions/graphql/types/donation.js +++ b/functions/graphql/types/donation.js @@ -106,31 +106,36 @@ const confirmDonateMutation = extendType({ const DonationsStats = objectType({ name: 'DonationsStats', definition(t) { - t.nonNull.int("prizes"); - t.nonNull.int("touranments"); - t.nonNull.int("donations"); - t.nonNull.int("applications"); + t.nonNull.string("prizes"); + t.nonNull.string("touranments"); + t.nonNull.string("donations"); + t.nonNull.string("applications"); }, }) const getDonationsStats = extendType({ type: "Query", definition(t) { - t.nonNull.list.nonNull.field('getDonationsStats', { + t.nonNull.field('getDonationsStats', { type: "DonationsStats", - resolve() { - return { - prizes: 2600, - touranments: 2, - donations: prisma.donation.aggregate({ + async resolve() { + const [donations, applications] = await Promise.all([ + prisma.donation.aggregate({ _sum: { amount: true }, where: { paid: true } - }), - applications: prisma.project.count() + }).then(d => d._sum.amount), + prisma.project.count()]); + + // #TODO add a measurement unit for prizes & donations (eg. $ or sats or BTC) + return { + prizes: 2600, + touranments: 2, + donations, + applications } } }) diff --git a/src/features/Donations/components/DonateCard/DonateCard.tsx b/src/features/Donations/components/DonateCard/DonateCard.tsx index 00f703a..ad7035b 100644 --- a/src/features/Donations/components/DonateCard/DonateCard.tsx +++ b/src/features/Donations/components/DonateCard/DonateCard.tsx @@ -57,7 +57,7 @@ export default function DonateCard() {
+ content:

Shock the Web is a virtual hackathon to promote, explore, build and design web applications that can interact with WebLN enabled wallets and browsers. We want to make building on bitcoin more accessible to the masses of web developers out there.
Bitcoin development can seem scary for new developers coming in, but it doesn't have to be. With the lightning network's toolkit and libraries a bunch of new opportunities are waiting to be explored. We hope these hackathons can be a chance for you to preview what is possible on bitcoin and the lightning network by fostering collaboration, hopefully shortening (or easing) any developer onboarding time, and helping you connect with other bitcoiners in a fun and friendly space. @@ -26,7 +26,7 @@ export default function DonatePage() { }, { heading: "Who is working on BOLT🔩FUN?", - content:

+ content:

Shock the Web is a virtual hackathon to promote, explore, build and design web applications that can interact with WebLN enabled wallets and browsers. We want to make building on bitcoin more accessible to the masses of web developers out there.
Bitcoin development can seem scary for new developers coming in, but it doesn't have to be. With the lightning network's toolkit and libraries a bunch of new opportunities are waiting to be explored. We hope these hackathons can be a chance for you to preview what is possible on bitcoin and the lightning network by fostering collaboration, hopefully shortening (or easing) any developer onboarding time, and helping you connect with other bitcoiners in a fun and friendly space. @@ -34,7 +34,7 @@ export default function DonatePage() { }, { heading: "How can makers win prizes?", - content:

+ content:

Shock the Web is a virtual hackathon to promote, explore, build and design web applications that can interact with WebLN enabled wallets and browsers. We want to make building on bitcoin more accessible to the masses of web developers out there.
Bitcoin development can seem scary for new developers coming in, but it doesn't have to be. With the lightning network's toolkit and libraries a bunch of new opportunities are waiting to be explored. We hope these hackathons can be a chance for you to preview what is possible on bitcoin and the lightning network by fostering collaboration, hopefully shortening (or easing) any developer onboarding time, and helping you connect with other bitcoiners in a fun and friendly space. @@ -42,7 +42,7 @@ export default function DonatePage() { }, { heading: "How can I donate?", - content:

+ content:

Shock the Web is a virtual hackathon to promote, explore, build and design web applications that can interact with WebLN enabled wallets and browsers. We want to make building on bitcoin more accessible to the masses of web developers out there.
Bitcoin development can seem scary for new developers coming in, but it doesn't have to be. With the lightning network's toolkit and libraries a bunch of new opportunities are waiting to be explored. We hope these hackathons can be a chance for you to preview what is possible on bitcoin and the lightning network by fostering collaboration, hopefully shortening (or easing) any developer onboarding time, and helping you connect with other bitcoiners in a fun and friendly space. diff --git a/src/features/Donations/pages/DonatePage/DonationStats/DonationStats.tsx b/src/features/Donations/pages/DonatePage/DonationStats/DonationStats.tsx index 237cc98..8805af9 100644 --- a/src/features/Donations/pages/DonatePage/DonationStats/DonationStats.tsx +++ b/src/features/Donations/pages/DonatePage/DonationStats/DonationStats.tsx @@ -1,32 +1,47 @@ import { BiCoinStack } from "react-icons/bi"; import { FiGrid } from "react-icons/fi"; import { IoMedalOutline, IoRocketOutline } from "react-icons/io5"; +import { useDonationsStatsQuery } from "src/graphql"; +import { generateList } from "src/utils/helperFunctions"; import StatCard from "../StatCard/StatCard"; +import StatCardSkeleton from "../StatCard/StatCard.Skeleton"; export default function DonationStats() { + + const donationsStatQuery = useDonationsStatsQuery(); + + + return ( -

- Donations} - value='$2.6k' - /> - Tournaments} - value='1' - /> - Prizes} - value='$2.5k' - /> - Applications} - value='36' - /> +
+ + {donationsStatQuery.loading && generateList(, 4)} + + {!donationsStatQuery.loading && + <> + Donations} + value={donationsStatQuery.data?.getDonationsStats.donations} + /> + Tournaments} + value={donationsStatQuery.data?.getDonationsStats.touranments} + /> + Prizes} + value={donationsStatQuery.data?.getDonationsStats.prizes} + /> + Applications} + value={donationsStatQuery.data?.getDonationsStats.applications} + /> + + }
) } diff --git a/src/features/Donations/pages/DonatePage/DonationStats/donationStats.graphql b/src/features/Donations/pages/DonatePage/DonationStats/donationStats.graphql new file mode 100644 index 0000000..c36f05d --- /dev/null +++ b/src/features/Donations/pages/DonatePage/DonationStats/donationStats.graphql @@ -0,0 +1,8 @@ +query DonationsStats { + getDonationsStats { + prizes + touranments + donations + applications + } +} diff --git a/src/features/Donations/pages/DonatePage/Header/Header.tsx b/src/features/Donations/pages/DonatePage/Header/Header.tsx index 91b6d57..549565b 100644 --- a/src/features/Donations/pages/DonatePage/Header/Header.tsx +++ b/src/features/Donations/pages/DonatePage/Header/Header.tsx @@ -6,7 +6,7 @@ import styles from './styles.module.scss' export default function Header() { return (
-
+

diff --git a/src/features/Donations/pages/DonatePage/Header/styles.module.scss b/src/features/Donations/pages/DonatePage/Header/styles.module.scss index 41b3dcd..d088f33 100644 --- a/src/features/Donations/pages/DonatePage/Header/styles.module.scss +++ b/src/features/Donations/pages/DonatePage/Header/styles.module.scss @@ -4,8 +4,10 @@ padding: 56px 0; min-height: calc(min(1080px, 90vh)); background: #ffecf9; - background: linear-gradient(40deg, white -5%, #ffb7d963 74%, #e3faff61 100%); + background: linear-gradient(40deg, white 7%, #ffdadaa3 62%, #d0f6ff5c 96%); + background-size: 120% 120%; + animation: Animation 3s ease infinite; display: grid; grid-template-areas: ". content ."; grid-template-columns: minmax(16px, 1fr) minmax(auto, 910px) minmax(16px, 1fr); @@ -14,3 +16,15 @@ grid-area: content; } } + +@keyframes Animation { + 0% { + background-position: 0% 20%; + } + 50% { + background-position: 20% 0%; + } + 100% { + background-position: 0% 20%; + } +} diff --git a/src/features/Donations/pages/DonatePage/StatCard/StatCard.Skeleton.tsx b/src/features/Donations/pages/DonatePage/StatCard/StatCard.Skeleton.tsx new file mode 100644 index 0000000..0750a79 --- /dev/null +++ b/src/features/Donations/pages/DonatePage/StatCard/StatCard.Skeleton.tsx @@ -0,0 +1,15 @@ +import Skeleton from 'react-loading-skeleton' + + +export default function StatCardSkeleton() { + return ( +
+

+ +

+

+ +

+
+ ) +} diff --git a/src/features/Donations/pages/DonatePage/StatCard/StatCard.stories.tsx b/src/features/Donations/pages/DonatePage/StatCard/StatCard.stories.tsx index 289b1e7..5c7379b 100644 --- a/src/features/Donations/pages/DonatePage/StatCard/StatCard.stories.tsx +++ b/src/features/Donations/pages/DonatePage/StatCard/StatCard.stories.tsx @@ -1,6 +1,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import { FiGrid } from 'react-icons/fi' import StatCard from './StatCard'; +import StatCardSkeleton from './StatCard.Skeleton'; export default { title: 'Donations/Donate Page/StatCard', @@ -20,4 +21,9 @@ Default.args = { value: '36' } +const LoadingTemplate: ComponentStory = (args) =>
+ +export const Loading = LoadingTemplate.bind({}); +Loading.args = { +} diff --git a/src/features/Donations/pages/DonatePage/StatCard/StatCard.tsx b/src/features/Donations/pages/DonatePage/StatCard/StatCard.tsx index 74d900c..d2a54c3 100644 --- a/src/features/Donations/pages/DonatePage/StatCard/StatCard.tsx +++ b/src/features/Donations/pages/DonatePage/StatCard/StatCard.tsx @@ -16,7 +16,7 @@ export default function StatCard(props: Props) {

{props.label}

-

+

{props.value}

diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index 7e55423..79067a0 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -76,10 +76,10 @@ export type Donation = { export type DonationsStats = { __typename?: 'DonationsStats'; - applications: Scalars['Int']; - donations: Scalars['Int']; - prizes: Scalars['Int']; - touranments: Scalars['Int']; + applications: Scalars['String']; + donations: Scalars['String']; + prizes: Scalars['String']; + touranments: Scalars['String']; }; export type Hackathon = { @@ -186,7 +186,7 @@ export type Query = { allTopics: Array; getAllHackathons: Array; getCategory: Category; - getDonationsStats: Array; + getDonationsStats: DonationsStats; getFeed: Array; getLnurlDetailsForProject: LnurlDetails; getPostById: Post; @@ -350,6 +350,11 @@ export type SearchProjectsQueryVariables = Exact<{ export type SearchProjectsQuery = { __typename?: 'Query', searchProjects: Array<{ __typename?: 'Project', id: number, thumbnail_image: string, title: string, category: { __typename?: 'Category', title: string, id: number } }> }; +export type DonationsStatsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type DonationsStatsQuery = { __typename?: 'Query', getDonationsStats: { __typename?: 'DonationsStats', prizes: string, touranments: string, donations: string, applications: string } }; + export type DonateMutationVariables = Exact<{ amountInSat: Scalars['Int']; }>; @@ -531,6 +536,43 @@ export function useSearchProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOpt export type SearchProjectsQueryHookResult = ReturnType; export type SearchProjectsLazyQueryHookResult = ReturnType; export type SearchProjectsQueryResult = Apollo.QueryResult; +export const DonationsStatsDocument = gql` + query DonationsStats { + getDonationsStats { + prizes + touranments + donations + applications + } +} + `; + +/** + * __useDonationsStatsQuery__ + * + * To run a query within a React component, call `useDonationsStatsQuery` and pass it any options that fit your needs. + * When your component renders, `useDonationsStatsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDonationsStatsQuery({ + * variables: { + * }, + * }); + */ +export function useDonationsStatsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(DonationsStatsDocument, options); + } +export function useDonationsStatsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(DonationsStatsDocument, options); + } +export type DonationsStatsQueryHookResult = ReturnType; +export type DonationsStatsLazyQueryHookResult = ReturnType; +export type DonationsStatsQueryResult = Apollo.QueryResult; export const DonateDocument = gql` mutation Donate($amountInSat: Int!) { donate(amount_in_sat: $amountInSat) { diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts index e6ee116..dd6dcca 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/handlers.ts @@ -25,6 +25,7 @@ import { GetHackathonsQueryVariables, AllTopicsQuery, AllTopicsQueryVariables, + DonationsStatsQuery, } from 'src/graphql' const delay = (ms = 1000) => new Promise((res) => setTimeout(res, ms + Math.random() * 1000)) @@ -172,4 +173,20 @@ export const handlers = [ ) }), + + graphql.query('DonationsStats', async (req, res, ctx) => { + await delay() + + return res( + ctx.data({ + getDonationsStats: { + applications: '32', + donations: "$2.6k", + prizes: "$2.5k", + touranments: "1", + } + }) + ) + }), + ] \ No newline at end of file diff --git a/src/utils/helperFunctions.tsx b/src/utils/helperFunctions.tsx index 2c5cd5b..79c3384 100644 --- a/src/utils/helperFunctions.tsx +++ b/src/utils/helperFunctions.tsx @@ -1,4 +1,4 @@ -import React, { ComponentProps, ComponentType, Suspense } from "react"; +import React, { ComponentProps, ComponentType, ReactNode, Suspense } from "react"; import { RotatingLines } from "react-loader-spinner"; export function random(min: number, max: number) { @@ -85,3 +85,8 @@ export function shuffle(_array: Array) { return array; } + + +export function generateList(component: React.ReactElement, cnt: number) { + return Array(cnt).fill(0).map((_, idx) => React.cloneElement(component, { key: idx })) +} \ No newline at end of file