Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
What's done so far:
- Created the new "subscription" view
- Recovered the menu link to this new page
- Show a table with the current subscription. It's been done as a table
  since it looks like it, but I'm not sure if it's useful considering
  there aren't plans for multiple subscriptions
- Removed the "open modal" temporary button from the main menu
- Such modal can now be opened using the "view plans & pricing" button
  in the new page
- A new Subscription Provider/Context has been created, with a
  `permissions` method in order to check for permissions of the current
  plan, but it has not been applied yet anywhere
- The pricing modal has been minimally changed to allow setting a custom
  title, and moving its contents into an independent component
- Created modals for the different tier/plan upgrade scenarios
  • Loading branch information
elboletaire committed Nov 26, 2024
1 parent c252b71 commit 1d79126
Show file tree
Hide file tree
Showing 30 changed files with 1,211 additions and 399 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@stripe/react-stripe-js": "^2.7.0",
"@stripe/stripe-js": "^3.3.0",
"@tanstack/react-query": "^5.59.15",
"@untitled-ui/icons-react": "^0.1.3",
"@vocdoni/chakra-components": "^0.9.6",
"@vocdoni/rainbowkit-wallets": "^0.2.2",
"@vocdoni/sdk": "~0.9.1",
Expand Down
5 changes: 4 additions & 1 deletion src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next'
import { useAccount, useWalletClient, WagmiConfig } from 'wagmi'
import { SaasAccountProvider } from '~components/Account/SaasAccountContext'
import { AuthProvider } from '~components/Auth/AuthContext'
import { SubscriptionProvider } from '~components/Auth/Subscription'
import { walletClientToSigner } from '~constants/wagmi-adapters'
import { VocdoniEnvironment } from './constants'
import { chains, wagmiConfig } from './constants/rainbow'
Expand All @@ -32,7 +33,9 @@ export const Providers = () => {

const SaasProviders = ({ children }: PropsWithChildren<{}>) => (
<AuthProvider>
<SaasAccountProvider>{children}</SaasAccountProvider>
<SubscriptionProvider>
<SaasAccountProvider>{children}</SaasAccountProvider>
</SubscriptionProvider>
</AuthProvider>
)

Expand Down
86 changes: 86 additions & 0 deletions src/components/Auth/Subscription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { createContext } from '@chakra-ui/react-utils'
import { useQuery } from '@tanstack/react-query'
import { useClient } from '@vocdoni/react-providers'
import { dotobject, ensure0x } from '@vocdoni/sdk'
import { ReactNode, useMemo } from 'react'
import { useAuth } from '~components/Auth/useAuth'
import { ApiEndpoints } from './api'

type PermissionsContextType = {
permission: (key: string) => any
subscription: SubscriptionType
loading: boolean
}

type SubscriptionType = {
subscriptionDetails: {
planID: number
startDate: string // ISO 8601 Date String
endDate: string // ISO 8601 Date String
renewalDate: string // ISO 8601 Date String
active: boolean
maxCensusSize: number
}
usage: {
sentSMS: number
sentEmails: number
subOrgs: number
members: number
}
plan: {
id: number
name: string
stripeID: string
default: boolean
organization: {
memberships: number
subOrgs: number
censusSize: number
}
votingTypes: {
approval: boolean
ranked: boolean
weighted: boolean
}
features: {
personalization: boolean
emailReminder: boolean
smsNotification: boolean
}
}
}

const [SubscriptionProvider, useSubscription] = createContext<PermissionsContextType>({
name: 'PermissionsContext',
errorMessage: 'usePermissions must be used within a PermissionsProvider',
})

const SubscriptionProviderComponent: React.FC<{ children: ReactNode }> = ({ children }) => {
const { bearedFetch } = useAuth()
const { account } = useClient()

// Fetch organization subscription details
// TODO: In the future, this may be merged with the role permissions (not yet defined)
const { data: subscription, isFetching } = useQuery({
queryKey: ['organizationSubscription', account?.address],
queryFn: () =>
bearedFetch<SubscriptionType>(
ApiEndpoints.OrganizationSubscription.replace('{address}', ensure0x(account?.address))
),
// Cache for 15 minutes
staleTime: 15 * 60 * 1000,
enabled: !!account?.address,
})

// Helper function to access permission using dot notation
const permission = useMemo(() => {
return (key: string) => {
if (!subscription || !subscription.plan) return undefined
return dotobject(subscription.plan, key)
}
}, [subscription])

return <SubscriptionProvider value={{ permission, subscription, loading: isFetching }} children={children} />
}

export { SubscriptionProviderComponent as SubscriptionProvider, useSubscription }
4 changes: 3 additions & 1 deletion src/components/Auth/api.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
type MethodTypes = 'GET' | 'POST' | 'PUT' | 'DELETE'

export enum ApiEndpoints {
InviteAccept = 'organizations/{address}/members/accept',
Login = 'auth/login',
Me = 'users/me',
InviteAccept = 'organizations/{address}/members/accept',
Organization = 'organizations/{address}',
OrganizationMembers = 'organizations/{address}/members',
OrganizationPendingMembers = 'organizations/{address}/members/pending',
Organizations = 'organizations',
OrganizationsRoles = 'organizations/roles',
OrganizationSubscription = 'organizations/{address}/subscription',
Password = 'users/password',
PasswordRecovery = 'users/password/recovery',
PasswordReset = 'users/password/reset',
Plans = 'plans',
Refresh = 'auth/refresh',
Register = 'users',
Verify = 'users/verify',
Expand Down
9 changes: 3 additions & 6 deletions src/components/Dashboard/Menu/Options.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Box, Button, Collapse, useDisclosure } from '@chakra-ui/react'
import { Box, Collapse, useDisclosure } from '@chakra-ui/react'
import { OrganizationName } from '@vocdoni/chakra-components'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { HiSquares2X2 } from 'react-icons/hi2'
import { IoIosSettings } from 'react-icons/io'
import { generatePath, matchPath, useLocation } from 'react-router-dom'
import { Routes } from '~src/router/routes'
import { PricingModal } from '../PricingModal'
import { DashboardMenuItem } from './Item'

type MenuItem = {
Expand Down Expand Up @@ -53,9 +52,9 @@ export const DashboardMenuOptions = () => {
icon: IoIosSettings,
children: [
{ label: t('organization.organization'), route: Routes.dashboard.organization },
{ label: t('team'), route: Routes.dashboard.team },
{ label: t('team.title'), route: Routes.dashboard.team },
// { label: t('billing'), route: '#billing' },
// { label: t('subscription'), route: '#subscription' },
{ label: t('subscription'), route: Routes.dashboard.subscription },
{ label: t('profile'), route: Routes.dashboard.profile },
],
},
Expand Down Expand Up @@ -84,8 +83,6 @@ export const DashboardMenuOptions = () => {

return (
<Box>
<PricingModal isOpenModal={isOpen} onCloseModal={onClose} />
<Button onClick={onOpen}>Open Modal</Button>
<OrganizationName color='text.secondary' mb={2.5} />
{menuItems.map((item, index) => (
<Box key={index}>
Expand Down
126 changes: 0 additions & 126 deletions src/components/Dashboard/PricingModal.tsx

This file was deleted.

71 changes: 0 additions & 71 deletions src/components/Organization/Dashboard/PricingCard.tsx

This file was deleted.

Loading

0 comments on commit 1d79126

Please sign in to comment.