From 05d0060dd3fc757a0e71258e045087908eb0a19d Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Fri, 3 Nov 2023 15:32:49 +0200 Subject: [PATCH] UI improvements (#21) * clean up env example files * use createTheme from widget * display price * Design improvements * Some more minor design improvements * Fixed the products grid * Removed test data * Product grid improvements * Grid fix --------- Co-authored-by: Mikk --- .env.example | 5 + apps/backend/.env.example | 16 +- .../superfluid-stripe-converter.controller.ts | 17 +- apps/frontend/.env.example | 9 +- apps/frontend/package.json | 2 +- apps/frontend/pnpm-lock.yaml | 134 ++----------- apps/frontend/src/components/Layout.tsx | 22 +-- apps/frontend/src/pages/[product].tsx | 19 +- apps/frontend/src/pages/index.tsx | 20 +- apps/frontend/src/pages/pricing.tsx | 184 ++++++++++-------- example.env | 4 - 11 files changed, 190 insertions(+), 242 deletions(-) create mode 100644 .env.example delete mode 100644 example.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..89f40f3 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +STRIPE_SECRET_KEY= +INTERNAL_API_KEY=my-secret-key +QUEUE_DASHBOARD_USER=user +QUEUE_DASHBOARD_PASSWORD=password +# If you're doing local development then you might prefer to use .env files in /backend and /frontend \ No newline at end of file diff --git a/apps/backend/.env.example b/apps/backend/.env.example index 8ebbbcd..aec4fc5 100644 --- a/apps/backend/.env.example +++ b/apps/backend/.env.example @@ -1,10 +1,10 @@ +STRIPE_SECRET_KEY= +STRIPE_TEST_MODE=true # setting this true will create an example project on Stripe first time REDIS_HOST=localhost REDIS_PORT=6379 -REDIS_USER= -REDIS_PASSWORD= -QUEUE_DASHBOARD_USER=user -QUEUE_DASHBOARD_PASSWORD=password -INTERNAL_API_KEY= -STRIPE_SECRET_KEY= -STRIPE_TEST_MODE=true -PORT=3001 \ No newline at end of file +REDIS_USER= # optional for local development +REDIS_PASSWORD= # optional for local development +PORT=3001 # optional, will default to 3001 +QUEUE_DASHBOARD_USER=user # optional, will default to STRIPE_SECRET_KEY +QUEUE_DASHBOARD_PASSWORD=password # optional, if not specified, use STRIPE_SECRET_KEY for username and leave password empty +INTERNAL_API_KEY=my-secret-key # optional, if not specified, use STRIPE_SECRET_KEY \ No newline at end of file diff --git a/apps/backend/src/superfluid-stripe-converter/superfluid-stripe-converter.controller.ts b/apps/backend/src/superfluid-stripe-converter/superfluid-stripe-converter.controller.ts index 4453316..628e13a 100644 --- a/apps/backend/src/superfluid-stripe-converter/superfluid-stripe-converter.controller.ts +++ b/apps/backend/src/superfluid-stripe-converter/superfluid-stripe-converter.controller.ts @@ -9,12 +9,20 @@ import { SuperfluidStripeConfigService, } from './superfluid-stripe-config/superfluid-stripe-config.service'; +type StripeProductWithPriceExpanded = Stripe.Product & { + default_price: Stripe.Price +} + type ProductResponse = { stripeProduct: Stripe.Product; productDetails: WidgetProps['productDetails']; paymentDetails: WidgetProps['paymentDetails']; }; +type ProductsResponse = (ProductResponse & { + stripeProduct: StripeProductWithPriceExpanded +})[] + @Controller('superfluid-stripe-converter') export class SuperfluidStripeConverterController { constructor( @@ -33,7 +41,7 @@ export class SuperfluidStripeConverterController { this.stripeClient.prices .list({ product: productId, - active: true, + active: true }) .autoPagingToArray(DEFAULT_PAGING), this.superfluidStripeConfigService.loadOrInitializeBlockchainConfig(), @@ -51,11 +59,12 @@ export class SuperfluidStripeConverterController { } @Get('products') - async products(): Promise { - const [stripeProducts, stripePrices, blockchainConfig] = await Promise.all([ + async products(): Promise { + const [stripeProducts_, stripePrices, blockchainConfig] = await Promise.all([ this.stripeClient.products .list({ active: true, + expand: ["data.default_price"] }) .autoPagingToArray(DEFAULT_PAGING), this.stripeClient.prices @@ -66,6 +75,8 @@ export class SuperfluidStripeConverterController { this.superfluidStripeConfigService.loadOrInitializeCompleteConfig(), ]); + const stripeProducts = stripeProducts_ as StripeProductWithPriceExpanded[]; + // check eligibility somewhere? const results = await Promise.all( diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example index 8a3e0a2..ca6ea2d 100644 --- a/apps/frontend/.env.example +++ b/apps/frontend/.env.example @@ -1,5 +1,6 @@ -NEXT_PUBLIC_WALLECT_CONNECT_PROJECT_ID=952483bf7a0f5ace4c40eb53967f1368 -BACKEND_PROTOCOL=http BACKEND_HOST=localhost -BACKEND_PORT=3001 -INTERNAL_API_KEY=my-secret-key \ No newline at end of file +BACKEND_PROTOCOL=http # optional +BACKEND_PORT=3001 # optional +NEXT_PUBLIC_WALLECT_CONNECT_PROJECT_ID=952483bf7a0f5ace4c40eb53967f1368 # optional, but strongly recommended, will default to WalletConnect v1 otherwise +INTERNAL_API_KEY=my-secret-key # optional, will fallback to STRIPE_SECRET_KEY +STRIPE_SECRET_KEY= # optional \ No newline at end of file diff --git a/apps/frontend/package.json b/apps/frontend/package.json index df147ce..df915ed 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -27,7 +27,7 @@ "@emotion/styled": "latest", "@mui/icons-material": "latest", "@mui/material": "latest", - "@superfluid-finance/widget": "0.0.0-dev-20231026111944", + "@superfluid-finance/widget": "0.0.0-dev-20231103081953", "clsx": "^2.0.0", "connectkit": "^1.5.3", "next": "latest", diff --git a/apps/frontend/pnpm-lock.yaml b/apps/frontend/pnpm-lock.yaml index 8122d0d..7160d17 100644 --- a/apps/frontend/pnpm-lock.yaml +++ b/apps/frontend/pnpm-lock.yaml @@ -24,8 +24,8 @@ dependencies: specifier: latest version: 5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) '@superfluid-finance/widget': - specifier: 0.0.0-dev-20231026111944 - version: 0.0.0-dev-20231026111944(@mui/material@5.14.16)(@types/react@18.2.34)(typescript@5.2.2)(viem@1.17.1)(wagmi@1.4.5) + specifier: 0.0.0-dev-20231103081953 + version: 0.0.0-dev-20231103081953(@mui/material@5.14.16)(@types/react@18.2.34)(typescript@5.2.2)(viem@1.17.1)(wagmi@1.4.5) clsx: specifier: ^2.0.0 version: 2.0.0 @@ -744,8 +744,8 @@ packages: dependencies: '@babel/runtime': 7.23.2 '@floating-ui/react-dom': 2.0.2(react-dom@18.2.0)(react@18.2.0) - '@mui/types': 7.2.7(@types/react@18.2.34) - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) + '@mui/types': 7.2.8(@types/react@18.2.34) + '@mui/utils': 5.14.16(@types/react@18.2.34)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.34 clsx: 2.0.0 @@ -821,10 +821,10 @@ packages: '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.34)(react@18.2.0) '@mui/base': 5.0.0-beta.18(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) '@mui/material': 5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0) - '@mui/types': 7.2.7(@types/react@18.2.34) - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) - '@mui/x-tree-view': 6.0.0-alpha.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/base@5.0.0-beta.18)(@mui/material@5.14.16)(@mui/system@5.14.15)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) + '@mui/system': 5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0) + '@mui/types': 7.2.8(@types/react@18.2.34) + '@mui/utils': 5.14.16(@types/react@18.2.34)(react@18.2.0) + '@mui/x-tree-view': 6.0.0-alpha.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/base@5.0.0-beta.18)(@mui/material@5.14.16)(@mui/system@5.14.16)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.34 clsx: 2.0.0 prop-types: 15.8.1 @@ -868,23 +868,6 @@ packages: react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false - /@mui/private-theming@5.14.15(@types/react@18.2.34)(react@18.2.0): - resolution: {integrity: sha512-V2Xh+Tu6A07NoSpup0P9m29GwvNMYl5DegsGWqlOTJyAV7cuuVjmVPqxgvL8xBng4R85xqIQJRMjtYYktoPNuQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) - '@types/react': 18.2.34 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /@mui/private-theming@5.14.16(@types/react@18.2.34)(react@18.2.0): resolution: {integrity: sha512-FNlL0pTSEBh8nXsVWreCHDSHk+jG8cBx1sxRbT8JVtL+PYbYPi802zfV4B00Kkf0LNRVRvAVQwojMWSR/MYGng==} engines: {node: '>=12.0.0'} @@ -902,28 +885,6 @@ packages: react: 18.2.0 dev: false - /@mui/styled-engine@5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-mbOjRf867BysNpexe5Z/P8s3bWzDPNowmKhi7gtNDP/LPEeqAfiDSuC4WPTXmtvse1dCl30Nl755OLUYuoi7Mw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.1(@types/react@18.2.34)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.34)(react@18.2.0) - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /@mui/styled-engine@5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): resolution: {integrity: sha512-FfvYvTG/Zd+KXMMImbcMYEeQAbONGuX5Vx3gBmmtB6KyA7Mvm9Pma1ly3R0gc44yeoFd+2wBjn1feS8h42HW5w==} engines: {node: '>=12.0.0'} @@ -946,36 +907,6 @@ packages: react: 18.2.0 dev: false - /@mui/system@5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0): - resolution: {integrity: sha512-zr0Gdk1RgKiEk+tCMB900LaOpEC8NaGvxtkmMdL/CXgkqQZSVZOt2PQsxJWaw7kE4YVkIe4VukFVc43qcq9u3w==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@emotion/react': 11.11.1(@types/react@18.2.34)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.34)(react@18.2.0) - '@mui/private-theming': 5.14.15(@types/react@18.2.34)(react@18.2.0) - '@mui/styled-engine': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) - '@mui/types': 7.2.7(@types/react@18.2.34) - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) - '@types/react': 18.2.34 - clsx: 2.0.0 - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /@mui/system@5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0): resolution: {integrity: sha512-uKnPfsDqDs8bbN54TviAuoGWOmFiQLwNZ3Wvj+OBkJCzwA6QnLb/sSeCB7Pk3ilH4h4jQ0BHtbR+Xpjy9wlOuA==} engines: {node: '>=12.0.0'} @@ -1006,17 +937,6 @@ packages: react: 18.2.0 dev: false - /@mui/types@7.2.7(@types/react@18.2.34): - resolution: {integrity: sha512-sofpWmcBqOlTzRbr1cLQuUDKaUYVZTw8ENQrtL39TECRNENEzwgnNPh6WMfqMZlMvf1Aj9DLg74XPjnLr0izUQ==} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 18.2.34 - dev: false - /@mui/types@7.2.8(@types/react@18.2.34): resolution: {integrity: sha512-9u0ji+xspl96WPqvrYJF/iO+1tQ1L5GTaDOeG3vCR893yy7VcWwRNiVMmPdPNpMDqx0WV1wtEW9OMwK9acWJzQ==} peerDependencies: @@ -1028,24 +948,6 @@ packages: '@types/react': 18.2.34 dev: false - /@mui/utils@5.14.15(@types/react@18.2.34)(react@18.2.0): - resolution: {integrity: sha512-QBfHovAvTa0J1jXuYDaXGk+Yyp7+Fm8GSqx6nK2JbezGqzCFfirNdop/+bL9Flh/OQ/64PeXcW4HGDdOge+n3A==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@types/prop-types': 15.7.8 - '@types/react': 18.2.34 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - /@mui/utils@5.14.16(@types/react@18.2.34)(react@18.2.0): resolution: {integrity: sha512-3xV31GposHkwRbQzwJJuooWpK2ybWdEdeUPtRjv/6vjomyi97F3+68l+QVj9tPTvmfSbr2sx5c/NuvDulrdRmA==} engines: {node: '>=12.0.0'} @@ -1064,7 +966,7 @@ packages: react-is: 18.2.0 dev: false - /@mui/x-tree-view@6.0.0-alpha.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/base@5.0.0-beta.18)(@mui/material@5.14.16)(@mui/system@5.14.15)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0): + /@mui/x-tree-view@6.0.0-alpha.1(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/base@5.0.0-beta.18)(@mui/material@5.14.16)(@mui/system@5.14.16)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1081,9 +983,9 @@ packages: '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.34)(react@18.2.0) '@mui/base': 5.0.0-beta.18(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) '@mui/material': 5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0) - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) - '@types/react-transition-group': 4.4.7 + '@mui/system': 5.14.16(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.34)(react@18.2.0) + '@mui/utils': 5.14.16(@types/react@18.2.34)(react@18.2.0) + '@types/react-transition-group': 4.4.8 clsx: 2.0.0 prop-types: 15.8.1 react: 18.2.0 @@ -1438,8 +1340,8 @@ packages: '@uniswap/token-lists': 1.0.0-beta.33 dev: false - /@superfluid-finance/widget@0.0.0-dev-20231026111944(@mui/material@5.14.16)(@types/react@18.2.34)(typescript@5.2.2)(viem@1.17.1)(wagmi@1.4.5): - resolution: {integrity: sha512-nPqPqhY5Yygo6LZmIWmyaqlvqzQx+4EuRnpIHVga1rksFHMBFi8R1q5qweDavMKG3xcabA047LtisDfSVQN4OQ==} + /@superfluid-finance/widget@0.0.0-dev-20231103081953(@mui/material@5.14.16)(@types/react@18.2.34)(typescript@5.2.2)(viem@1.17.1)(wagmi@1.4.5): + resolution: {integrity: sha512-c8EUCRwXTdCC1db29CEA/y7YNcN/JAZBmj11Zx1MlNWmdG6kfyUQoPZeyJe68okayvf+Bh0R8qrqMNDYgxZ1Pw==} peerDependencies: viem: '>=1' wagmi: '>=1' @@ -1449,7 +1351,7 @@ packages: '@hookform/resolvers': 3.3.2(react-hook-form@7.47.0) '@mui/icons-material': 5.14.16(@mui/material@5.14.16)(@types/react@18.2.34)(react@18.2.0) '@mui/lab': 5.0.0-alpha.147(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@mui/material@5.14.16)(@types/react@18.2.34)(react-dom@18.2.0)(react@18.2.0) - '@mui/utils': 5.14.15(@types/react@18.2.34)(react@18.2.0) + '@mui/utils': 5.14.16(@types/react@18.2.34)(react@18.2.0) '@superfluid-finance/metadata': 1.1.18 '@superfluid-finance/tokenlist': 3.3.2 abitype: 0.10.1(typescript@5.2.2)(zod@3.22.4) @@ -1563,12 +1465,6 @@ packages: resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==} dev: false - /@types/react-transition-group@4.4.7: - resolution: {integrity: sha512-ICCyBl5mvyqYp8Qeq9B5G/fyBSRC0zx3XM3sCC6KkcMsNeAHqXBKkmat4GqdJET5jtYUpZXrxI5flve5qhi2Eg==} - dependencies: - '@types/react': 18.2.34 - dev: false - /@types/react-transition-group@4.4.8: resolution: {integrity: sha512-QmQ22q+Pb+HQSn04NL3HtrqHwYMf4h3QKArOy5F8U5nEVMaihBs3SR10WiOM1iwPz5jIo8x/u11al+iEGZZrvg==} dependencies: diff --git a/apps/frontend/src/components/Layout.tsx b/apps/frontend/src/components/Layout.tsx index 0083002..1b597c5 100644 --- a/apps/frontend/src/components/Layout.tsx +++ b/apps/frontend/src/components/Layout.tsx @@ -1,14 +1,6 @@ -import { - Box, - CssBaseline, - Paper, - Stack, - Theme, - ThemeOptions, - ThemeProvider, - createTheme, -} from '@mui/material'; -import { PropsWithChildren, useEffect, useMemo } from 'react'; +import { Stack, ThemeOptions, ThemeProvider } from '@mui/material'; +import { createWidgetTheme } from '@superfluid-finance/widget'; +import { PropsWithChildren, useMemo } from 'react'; type Props = PropsWithChildren<{ themeOptions: ThemeOptions; @@ -16,16 +8,16 @@ type Props = PropsWithChildren<{ export default function Layout({ children, themeOptions }: Props) { // TODO(KK): optimize, expose theme from widget? - const theme = useMemo(() => createTheme(themeOptions), [themeOptions]); + const theme = useMemo(() => createWidgetTheme(themeOptions), [themeOptions]); return ( {children} diff --git a/apps/frontend/src/pages/[product].tsx b/apps/frontend/src/pages/[product].tsx index e085e2e..27d79c3 100644 --- a/apps/frontend/src/pages/[product].tsx +++ b/apps/frontend/src/pages/[product].tsx @@ -3,13 +3,24 @@ import Layout from '@/components/Layout'; import SupefluidWidgetProvider from '@/components/SuperfluidWidgetProvider'; import WagmiProvider from '@/components/WagmiProvider'; import internalConfig from '@/internalConfig'; -import { Box, ThemeOptions } from '@mui/material'; +import { + Box, + Button, + Container, + IconButton, + Link, + Stack, + ThemeOptions, + Toolbar, +} from '@mui/material'; import { WidgetProps } from '@superfluid-finance/widget'; import { GetServerSideProps } from 'next'; import { use, useEffect, useState } from 'react'; import { paths } from '@/backend-openapi-client'; import createClient from 'openapi-fetch'; import { LookAndFeelConfig, ProductConfig } from './pricing'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import NextLink from 'next/link'; type Props = { productConfig: ProductConfig; @@ -25,6 +36,12 @@ export default function Product({ productConfig, theme }: Props) { // TODO(KK): handle theme any return ( + + + + {!!productConfig && mounted && ( diff --git a/apps/frontend/src/pages/index.tsx b/apps/frontend/src/pages/index.tsx index 2611837..19699b7 100644 --- a/apps/frontend/src/pages/index.tsx +++ b/apps/frontend/src/pages/index.tsx @@ -4,7 +4,7 @@ import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Link from '../Link'; import Layout from '@/components/Layout'; -import { ThemeOptions } from '@mui/material'; +import { Button, ThemeOptions } from '@mui/material'; import { GetServerSideProps } from 'next'; import createClient from 'openapi-fetch'; import { paths } from '@/backend-openapi-client'; @@ -16,7 +16,7 @@ type Props = { theme: ThemeOptions }; export default function Home({ theme }: Props) { return ( - + - + Superfluid ♥ Stripe - + The Superfluid-Stripe Integration provides a bridge between the conventional and the progressive world of digital finance, refining the process of managing subscription-based services. @@ -45,9 +45,15 @@ export default function Home({ theme }: Props) { height: '100%', }} > - - Pricing Table - + diff --git a/apps/frontend/src/pages/pricing.tsx b/apps/frontend/src/pages/pricing.tsx index 726d733..0338fa9 100644 --- a/apps/frontend/src/pages/pricing.tsx +++ b/apps/frontend/src/pages/pricing.tsx @@ -1,22 +1,23 @@ -import { ThemeOptions } from '@mui/material/styles'; +import { paths } from '@/backend-openapi-client'; +import Layout from '@/components/Layout'; +import internalConfig from '@/internalConfig'; +import StarIcon from '@mui/icons-material/StarBorder'; +import { List, ListItem, ListItemIcon, ListItemText, Stack, useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; import CardActions from '@mui/material/CardActions'; import CardContent from '@mui/material/CardContent'; import CardHeader from '@mui/material/CardHeader'; -import Grid from '@mui/material/Grid'; -import StarIcon from '@mui/icons-material/StarBorder'; -import Typography from '@mui/material/Typography'; import Container from '@mui/material/Container'; -import Layout from '@/components/Layout'; +import Typography from '@mui/material/Typography'; +import { ThemeOptions } from '@mui/material/styles'; +import { WidgetProps } from '@superfluid-finance/widget'; import { GetServerSideProps } from 'next'; import createClient from 'openapi-fetch'; -import { paths } from '@/backend-openapi-client'; -import internalConfig from '@/internalConfig'; -import { WidgetProps } from '@superfluid-finance/widget'; +import { FC, useMemo } from 'react'; import Stripe from 'stripe'; -import { useMemo } from 'react'; +import ArrowRightIcon from '@mui/icons-material/ArrowRight'; type Tier = { title: string; @@ -40,17 +41,92 @@ type Props = { theme: ThemeOptions; }; +const TierCard: FC<{ tier: Tier }> = ({ tier }) => { + const theme = useTheme(); + + // Enterprise card is full width at sm breakpoint + return ( + + : null} + subheaderTypographyProps={{ + align: 'center', + }} + sx={{ + backgroundColor: (theme) => + theme.palette.mode === 'light' ? theme.palette.grey[200] : theme.palette.grey[700], + }} + /> + + + + {tier.price} + + + /mo + + + + {tier.description.map((line) => ( + + + + + ))} + + + + + + + ); +}; + export default function Pricing({ productConfigs, theme }: Props) { const tiers = useMemo( () => - productConfigs.map((p) => ({ - title: p.stripeProduct.name, - description: p.stripeProduct.features.map((f) => f.name), - price: 'X', - buttonText: 'Get Started', - buttonVariant: 'contained', - productId: p.stripeProduct.id, - })), + productConfigs.map((x) => { + const price = x.stripeProduct.default_price as Stripe.Price | null; + return { + title: x.stripeProduct.name, + description: x.stripeProduct.features.map((f) => f.name), + price: + price && price.unit_amount + ? new Intl.NumberFormat('en-US', { + style: 'currency', + currency: price.currency, + }).format(price.unit_amount) + : '', + buttonText: 'Get Started', + buttonVariant: 'contained', + productId: x.stripeProduct.id, + }; + }), [productConfigs], ); @@ -61,77 +137,25 @@ export default function Pricing({ productConfigs, theme }: Props) { Pricing - + Quickly build an effective pricing table for your potential customers with this layout. It's built with default MUI components with little customization. {/* End hero unit */} - + {tiers.map((tier) => ( - // Enterprise card is full width at sm breakpoint - - - : null} - subheaderTypographyProps={{ - align: 'center', - }} - sx={{ - backgroundColor: (theme) => - theme.palette.mode === 'light' - ? theme.palette.grey[200] - : theme.palette.grey[700], - }} - /> - - - - ${tier.price} - - - /mo - - -
    - {tier.description.map((line) => ( - - {/* TODO(KK): clean up */} - {/* {line} */} - - ))} -
-
- - - -
-
+ ))} -
+
); diff --git a/example.env b/example.env deleted file mode 100644 index 009a867..0000000 --- a/example.env +++ /dev/null @@ -1,4 +0,0 @@ -STRIPE_SECRET_KEY= -INTERNAL_API_KEY= -QUEUE_DASHBOARD_USER=user -QUEUE_DASHBOARD_PASSWORD=password