first commit
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
CustomSubscription,
|
||||
ManagedGroupSubscription,
|
||||
MemberGroupSubscription,
|
||||
PaidSubscription,
|
||||
} from '../../../../../types/subscription/dashboard/subscription'
|
||||
import {
|
||||
Plan,
|
||||
PriceForDisplayData,
|
||||
} from '../../../../../types/subscription/plan'
|
||||
import { Institution } from '../../../../../types/institution'
|
||||
import getMeta from '../../../utils/meta'
|
||||
import {
|
||||
loadDisplayPriceWithTaxPromise,
|
||||
loadGroupDisplayPriceWithTaxPromise,
|
||||
} from '../util/recurly-pricing'
|
||||
import { isRecurlyLoaded } from '../util/is-recurly-loaded'
|
||||
import { SubscriptionDashModalIds } from '../../../../../types/subscription/dashboard/modal-ids'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { formatCurrency } from '@/shared/utils/currency'
|
||||
import { ManagedInstitution } from '../../../../../types/subscription/dashboard/managed-institution'
|
||||
import { Publisher } from '../../../../../types/subscription/dashboard/publisher'
|
||||
import { formatTime } from '@/features/utils/format-date'
|
||||
|
||||
type SubscriptionDashboardContextValue = {
|
||||
groupPlanToChangeToCode: string
|
||||
groupPlanToChangeToSize: string
|
||||
groupPlanToChangeToUsage: string
|
||||
groupPlanToChangeToPrice?: PriceForDisplayData
|
||||
groupPlanToChangeToPriceError?: boolean
|
||||
handleCloseModal: () => void
|
||||
handleOpenModal: (
|
||||
modalIdToOpen: SubscriptionDashModalIds,
|
||||
planCode?: string
|
||||
) => void
|
||||
hasDisplayedSubscription: boolean
|
||||
hasValidActiveSubscription: boolean
|
||||
institutionMemberships?: Institution[]
|
||||
managedGroupSubscriptions: ManagedGroupSubscription[]
|
||||
memberGroupSubscriptions: MemberGroupSubscription[]
|
||||
managedInstitutions: ManagedInstitution[]
|
||||
managedPublishers: Publisher[]
|
||||
updateManagedInstitution: (institution: ManagedInstitution) => void
|
||||
modalIdShown?: SubscriptionDashModalIds
|
||||
personalSubscription?: PaidSubscription | CustomSubscription
|
||||
hasSubscription: boolean
|
||||
plans: Plan[]
|
||||
planCodeToChangeTo?: string
|
||||
queryingGroupPlanToChangeToPrice: boolean
|
||||
queryingIndividualPlansData: boolean
|
||||
recurlyLoadError: boolean
|
||||
setGroupPlanToChangeToCode: React.Dispatch<React.SetStateAction<string>>
|
||||
setGroupPlanToChangeToSize: React.Dispatch<React.SetStateAction<string>>
|
||||
setGroupPlanToChangeToUsage: React.Dispatch<React.SetStateAction<string>>
|
||||
setModalIdShown: React.Dispatch<
|
||||
React.SetStateAction<SubscriptionDashModalIds | undefined>
|
||||
>
|
||||
setPlanCodeToChangeTo: React.Dispatch<
|
||||
React.SetStateAction<string | undefined>
|
||||
>
|
||||
setRecurlyLoadError: React.Dispatch<React.SetStateAction<boolean>>
|
||||
showCancellation: boolean
|
||||
setShowCancellation: React.Dispatch<React.SetStateAction<boolean>>
|
||||
leavingGroupId?: string
|
||||
setLeavingGroupId: React.Dispatch<React.SetStateAction<string | undefined>>
|
||||
userCanExtendTrial: boolean
|
||||
getFormattedRenewalDate: () => string
|
||||
}
|
||||
|
||||
export const SubscriptionDashboardContext = createContext<
|
||||
SubscriptionDashboardContextValue | undefined
|
||||
>(undefined)
|
||||
|
||||
export function SubscriptionDashboardProvider({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode
|
||||
}) {
|
||||
const { i18n } = useTranslation()
|
||||
const [modalIdShown, setModalIdShown] = useState<
|
||||
SubscriptionDashModalIds | undefined
|
||||
>()
|
||||
const [recurlyLoadError, setRecurlyLoadError] = useState(false)
|
||||
const [showCancellation, setShowCancellation] = useState(false)
|
||||
const [plans, setPlans] = useState<Plan[]>([])
|
||||
const [queryingIndividualPlansData, setQueryingIndividualPlansData] =
|
||||
useState(true)
|
||||
const [planCodeToChangeTo, setPlanCodeToChangeTo] = useState<
|
||||
string | undefined
|
||||
>()
|
||||
const [groupPlanToChangeToSize, setGroupPlanToChangeToSize] = useState('10')
|
||||
const [groupPlanToChangeToCode, setGroupPlanToChangeToCode] =
|
||||
useState('collaborator')
|
||||
const [groupPlanToChangeToUsage, setGroupPlanToChangeToUsage] =
|
||||
useState('enterprise')
|
||||
const [
|
||||
queryingGroupPlanToChangeToPrice,
|
||||
setQueryingGroupPlanToChangeToPrice,
|
||||
] = useState(false)
|
||||
const [groupPlanToChangeToPrice, setGroupPlanToChangeToPrice] =
|
||||
useState<PriceForDisplayData>()
|
||||
const [groupPlanToChangeToPriceError, setGroupPlanToChangeToPriceError] =
|
||||
useState(false)
|
||||
const [leavingGroupId, setLeavingGroupId] = useState<string | undefined>()
|
||||
|
||||
const plansWithoutDisplayPrice = getMeta('ol-plans')
|
||||
const institutionMemberships = getMeta('ol-currentInstitutionsWithLicence')
|
||||
const personalSubscription = getMeta('ol-subscription')
|
||||
const userCanExtendTrial = getMeta('ol-userCanExtendTrial')
|
||||
const managedGroupSubscriptions = getMeta('ol-managedGroupSubscriptions')
|
||||
const memberGroupSubscriptions = getMeta('ol-memberGroupSubscriptions')
|
||||
const [managedInstitutions, setManagedInstitutions] = useState(
|
||||
getMeta('ol-managedInstitutions')
|
||||
)
|
||||
const managedPublishers = getMeta('ol-managedPublishers')
|
||||
const hasSubscription = getMeta('ol-hasSubscription')
|
||||
const recurlyApiKey = getMeta('ol-recurlyApiKey')
|
||||
|
||||
const hasDisplayedSubscription = Boolean(
|
||||
institutionMemberships?.length > 0 ||
|
||||
personalSubscription ||
|
||||
memberGroupSubscriptions?.length > 0 ||
|
||||
managedGroupSubscriptions?.length > 0 ||
|
||||
managedInstitutions?.length > 0 ||
|
||||
managedPublishers?.length > 0
|
||||
)
|
||||
|
||||
const hasValidActiveSubscription = Boolean(
|
||||
['active', 'canceled'].includes(personalSubscription?.payment?.state) ||
|
||||
institutionMemberships?.length > 0 ||
|
||||
memberGroupSubscriptions?.length > 0
|
||||
)
|
||||
|
||||
const getFormattedRenewalDate = useCallback(() => {
|
||||
if (
|
||||
!personalSubscription.payment.pausedAt ||
|
||||
!personalSubscription.payment.remainingPauseCycles
|
||||
) {
|
||||
return personalSubscription.payment.nextPaymentDueAt
|
||||
}
|
||||
const pausedDate = new Date(personalSubscription.payment.pausedAt)
|
||||
pausedDate.setMonth(
|
||||
pausedDate.getMonth() + personalSubscription.payment.remainingPauseCycles
|
||||
)
|
||||
return formatTime(pausedDate, 'MMMM Do, YYYY')
|
||||
}, [personalSubscription])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isRecurlyLoaded()) {
|
||||
setRecurlyLoadError(true)
|
||||
} else if (recurlyApiKey) {
|
||||
recurly.configure(recurlyApiKey)
|
||||
}
|
||||
}, [recurlyApiKey, setRecurlyLoadError])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isRecurlyLoaded() &&
|
||||
plansWithoutDisplayPrice &&
|
||||
personalSubscription?.payment
|
||||
) {
|
||||
const { currency, taxRate } = personalSubscription.payment
|
||||
const fetchPlansDisplayPrices = async () => {
|
||||
for (const plan of plansWithoutDisplayPrice) {
|
||||
try {
|
||||
const priceData = await loadDisplayPriceWithTaxPromise(
|
||||
plan.planCode,
|
||||
currency,
|
||||
taxRate,
|
||||
i18n.language
|
||||
)
|
||||
if (priceData?.totalAsNumber !== undefined) {
|
||||
plan.displayPrice = formatCurrency(
|
||||
priceData.totalAsNumber,
|
||||
currency,
|
||||
i18n.language
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
debugConsole.error(error)
|
||||
}
|
||||
}
|
||||
setPlans(plansWithoutDisplayPrice)
|
||||
setQueryingIndividualPlansData(false)
|
||||
}
|
||||
fetchPlansDisplayPrices().catch(debugConsole.error)
|
||||
}
|
||||
}, [personalSubscription, plansWithoutDisplayPrice, i18n.language])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isRecurlyLoaded() &&
|
||||
groupPlanToChangeToCode &&
|
||||
groupPlanToChangeToSize &&
|
||||
groupPlanToChangeToUsage &&
|
||||
personalSubscription?.payment
|
||||
) {
|
||||
setQueryingGroupPlanToChangeToPrice(true)
|
||||
|
||||
const { currency, taxRate } = personalSubscription.payment
|
||||
const fetchGroupDisplayPrice = async () => {
|
||||
setGroupPlanToChangeToPriceError(false)
|
||||
let priceData
|
||||
try {
|
||||
priceData = await loadGroupDisplayPriceWithTaxPromise(
|
||||
groupPlanToChangeToCode,
|
||||
currency,
|
||||
taxRate,
|
||||
groupPlanToChangeToSize,
|
||||
groupPlanToChangeToUsage,
|
||||
i18n.language
|
||||
)
|
||||
} catch (e) {
|
||||
debugConsole.error(e)
|
||||
setGroupPlanToChangeToPriceError(true)
|
||||
}
|
||||
setQueryingGroupPlanToChangeToPrice(false)
|
||||
setGroupPlanToChangeToPrice(priceData)
|
||||
}
|
||||
fetchGroupDisplayPrice()
|
||||
}
|
||||
}, [
|
||||
groupPlanToChangeToUsage,
|
||||
groupPlanToChangeToSize,
|
||||
personalSubscription,
|
||||
groupPlanToChangeToCode,
|
||||
i18n.language,
|
||||
])
|
||||
|
||||
const updateManagedInstitution = useCallback(
|
||||
(institution: ManagedInstitution) => {
|
||||
setManagedInstitutions(institutions => {
|
||||
return [
|
||||
...(institutions || []).map(i =>
|
||||
i.v1Id === institution.v1Id ? institution : i
|
||||
),
|
||||
]
|
||||
})
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const handleCloseModal = useCallback(() => {
|
||||
setModalIdShown(undefined)
|
||||
setPlanCodeToChangeTo(undefined)
|
||||
}, [setModalIdShown, setPlanCodeToChangeTo])
|
||||
|
||||
const handleOpenModal = useCallback(
|
||||
(id, planCode) => {
|
||||
setModalIdShown(id)
|
||||
setPlanCodeToChangeTo(planCode)
|
||||
},
|
||||
[setModalIdShown, setPlanCodeToChangeTo]
|
||||
)
|
||||
|
||||
const value = useMemo<SubscriptionDashboardContextValue>(
|
||||
() => ({
|
||||
groupPlanToChangeToCode,
|
||||
groupPlanToChangeToPrice,
|
||||
groupPlanToChangeToPriceError,
|
||||
groupPlanToChangeToSize,
|
||||
groupPlanToChangeToUsage,
|
||||
handleCloseModal,
|
||||
handleOpenModal,
|
||||
hasDisplayedSubscription,
|
||||
hasValidActiveSubscription,
|
||||
institutionMemberships,
|
||||
managedGroupSubscriptions,
|
||||
memberGroupSubscriptions,
|
||||
managedInstitutions,
|
||||
managedPublishers,
|
||||
updateManagedInstitution,
|
||||
modalIdShown,
|
||||
personalSubscription,
|
||||
hasSubscription,
|
||||
plans,
|
||||
planCodeToChangeTo,
|
||||
queryingGroupPlanToChangeToPrice,
|
||||
queryingIndividualPlansData,
|
||||
recurlyLoadError,
|
||||
setGroupPlanToChangeToCode,
|
||||
setGroupPlanToChangeToSize,
|
||||
setGroupPlanToChangeToUsage,
|
||||
setModalIdShown,
|
||||
setPlanCodeToChangeTo,
|
||||
setRecurlyLoadError,
|
||||
showCancellation,
|
||||
setShowCancellation,
|
||||
leavingGroupId,
|
||||
setLeavingGroupId,
|
||||
userCanExtendTrial,
|
||||
getFormattedRenewalDate,
|
||||
}),
|
||||
[
|
||||
groupPlanToChangeToCode,
|
||||
groupPlanToChangeToPrice,
|
||||
groupPlanToChangeToPriceError,
|
||||
groupPlanToChangeToSize,
|
||||
groupPlanToChangeToUsage,
|
||||
handleCloseModal,
|
||||
handleOpenModal,
|
||||
hasDisplayedSubscription,
|
||||
hasValidActiveSubscription,
|
||||
institutionMemberships,
|
||||
managedGroupSubscriptions,
|
||||
memberGroupSubscriptions,
|
||||
managedInstitutions,
|
||||
managedPublishers,
|
||||
updateManagedInstitution,
|
||||
modalIdShown,
|
||||
personalSubscription,
|
||||
hasSubscription,
|
||||
plans,
|
||||
planCodeToChangeTo,
|
||||
queryingGroupPlanToChangeToPrice,
|
||||
queryingIndividualPlansData,
|
||||
recurlyLoadError,
|
||||
setGroupPlanToChangeToCode,
|
||||
setGroupPlanToChangeToSize,
|
||||
setGroupPlanToChangeToUsage,
|
||||
setModalIdShown,
|
||||
setPlanCodeToChangeTo,
|
||||
setRecurlyLoadError,
|
||||
showCancellation,
|
||||
setShowCancellation,
|
||||
leavingGroupId,
|
||||
setLeavingGroupId,
|
||||
userCanExtendTrial,
|
||||
getFormattedRenewalDate,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<SubscriptionDashboardContext.Provider value={value}>
|
||||
{children}
|
||||
</SubscriptionDashboardContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useSubscriptionDashboardContext() {
|
||||
const context = useContext(SubscriptionDashboardContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'SubscriptionDashboardContext is only available inside SubscriptionDashboardProvider'
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
Reference in New Issue
Block a user