-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
8b61573
commit 83f1757
Showing
14 changed files
with
438 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.plan) return undefined | ||
return dotobject(subscription.plan, key) | ||
} | ||
}, [subscription]) | ||
|
||
return <SubscriptionProvider value={{ permission, subscription, loading: isFetching }} children={children} /> | ||
} | ||
|
||
export { SubscriptionProviderComponent as SubscriptionProvider, useSubscription } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { | ||
Avatar, | ||
Button, | ||
Progress, | ||
Table, | ||
TableContainer, | ||
Tag, | ||
Tbody, | ||
Td, | ||
Th, | ||
Thead, | ||
Tr, | ||
useDisclosure, | ||
} from '@chakra-ui/react' | ||
import { Trans, useTranslation } from 'react-i18next' | ||
import { useSubscription } from '~components/Auth/Subscription' | ||
import { SubscriptionModal } from '~components/Dashboard/PricingModal' | ||
|
||
export const Subscription = () => { | ||
const { t } = useTranslation() | ||
const { isOpen, onClose, onOpen } = useDisclosure() | ||
|
||
return ( | ||
<> | ||
<SubscriptionModal isOpenModal={isOpen} onCloseModal={onClose} title={t('pricing_modal.title')} /> | ||
<Button onClick={onOpen} alignSelf='end'> | ||
View Plans & Pricing | ||
</Button> | ||
<SubscriptionList /> | ||
</> | ||
) | ||
} | ||
|
||
export const SubscriptionList = () => { | ||
const { subscription, loading } = useSubscription() | ||
|
||
if (loading) { | ||
return <Progress size='xs' isIndeterminate /> | ||
} | ||
|
||
if (!subscription) { | ||
return null | ||
} | ||
|
||
return ( | ||
<TableContainer> | ||
<Table size='sm'> | ||
<Thead> | ||
<Tr> | ||
<Th> | ||
<Trans i18nKey='subscription.your_subscription'>Your Subscription</Trans> | ||
</Th> | ||
<Th> | ||
<Trans i18nKey='subscription.price'>Price</Trans> | ||
</Th> | ||
<Th> | ||
<Trans i18nKey='subscription.since'>Since</Trans> | ||
</Th> | ||
<Th colSpan={2}> | ||
<Trans i18nKey='subscription.next_billing'>Next Billing</Trans> | ||
</Th> | ||
</Tr> | ||
</Thead> | ||
<Tbody> | ||
<Tr> | ||
<Td display='flex' alignItems='center' gap={3}> | ||
<Avatar name={subscription.plan.name} size='sm' /> | ||
{subscription.plan.name} ({subscription.plan.organization.memberships} members) | ||
</Td> | ||
<Td> | ||
<Tag>undefined</Tag> | ||
</Td> | ||
<Td> | ||
<Tag>{new Date(subscription.subscriptionDetails.startDate).toLocaleDateString()}</Tag> | ||
</Td> | ||
<Td> | ||
<Tag>{new Date(subscription.subscriptionDetails.renewalDate).toLocaleDateString()}</Tag> | ||
</Td> | ||
<Td> | ||
<Button variant='outline' size='sm'> | ||
<Trans i18nKey='subscription.change_plan_button'>Change</Trans> | ||
</Button> | ||
</Td> | ||
</Tr> | ||
</Tbody> | ||
</Table> | ||
</TableContainer> | ||
) | ||
} | ||
|
||
export const SubscriptionHistory = () => {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { useEffect } from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import { useOutletContext } from 'react-router-dom' | ||
import { DashboardContents } from '~components/Layout/Dashboard' | ||
import { Subscription } from '~components/Organization/Subscription' | ||
import { DashboardLayoutContext } from '~elements/LayoutDashboard' | ||
|
||
const SubscriptionPage = () => { | ||
const { t } = useTranslation() | ||
const { setTitle } = useOutletContext<DashboardLayoutContext>() | ||
|
||
useEffect(() => { | ||
setTitle(t('subscription', { defaultValue: 'Subscription' })) | ||
}, []) | ||
|
||
return ( | ||
<DashboardContents display='flex' flexDir='column'> | ||
<Subscription /> | ||
</DashboardContents> | ||
) | ||
} | ||
|
||
export default SubscriptionPage |
Oops, something went wrong.