import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState, } from 'react' import { ManagedGroupSubscription, Subscription, } from '../../../../../types/subscription/dashboard/subscription' import { Plan, PriceForDisplayData, } from '../../../../../types/subscription/plan' import { Institution as ManagedInstitution } from '../components/dashboard/managed-institutions' 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' type SubscriptionDashboardContextValue = { groupPlanToChangeToCode?: string groupPlanToChangeToSize: string groupPlanToChangeToUsage?: string groupPlanToChangeToPrice?: PriceForDisplayData groupPlanToChangeToPriceError?: boolean handleCloseModal: () => void handleOpenModal: ( modalIdToOpen: SubscriptionDashModalIds, planCode?: string ) => void hasDisplayedSubscription: boolean institutionMemberships?: Institution[] managedGroupSubscriptions: ManagedGroupSubscription[] managedInstitutions: ManagedInstitution[] updateManagedInstitution: (institution: ManagedInstitution) => void modalIdShown?: SubscriptionDashModalIds personalSubscription?: Subscription plans: Plan[] planCodeToChangeTo?: string queryingGroupPlanToChangeToPrice: boolean queryingIndividualPlansData: boolean recurlyLoadError: boolean setGroupPlanToChangeToCode: React.Dispatch< React.SetStateAction > setGroupPlanToChangeToSize: React.Dispatch> setGroupPlanToChangeToUsage: React.Dispatch> setModalIdShown: React.Dispatch< React.SetStateAction > setPlanCodeToChangeTo: React.Dispatch< React.SetStateAction > setRecurlyLoadError: React.Dispatch> showCancellation: boolean setShowCancellation: React.Dispatch> showChangePersonalPlan: boolean setShowChangePersonalPlan: React.Dispatch> } export const SubscriptionDashboardContext = createContext< SubscriptionDashboardContextValue | undefined >(undefined) export function SubscriptionDashboardProvider({ children, }: { children: ReactNode }) { const [modalIdShown, setModalIdShown] = useState< SubscriptionDashModalIds | undefined >() const [recurlyLoadError, setRecurlyLoadError] = useState(false) const [showCancellation, setShowCancellation] = useState(false) const [showChangePersonalPlan, setShowChangePersonalPlan] = useState(false) const [plans, setPlans] = useState([]) const [queryingIndividualPlansData, setQueryingIndividualPlansData] = useState(true) const [planCodeToChangeTo, setPlanCodeToChangeTo] = useState< string | undefined >() const [groupPlanToChangeToSize, setGroupPlanToChangeToSize] = useState('10') const [groupPlanToChangeToCode, setGroupPlanToChangeToCode] = useState< string | undefined >() const [groupPlanToChangeToUsage, setGroupPlanToChangeToUsage] = useState('enterprise') const [ queryingGroupPlanToChangeToPrice, setQueryingGroupPlanToChangeToPrice, ] = useState(false) const [groupPlanToChangeToPrice, setGroupPlanToChangeToPrice] = useState() const [groupPlanToChangeToPriceError, setGroupPlanToChangeToPriceError] = useState(false) const plansWithoutDisplayPrice = getMeta('ol-plans') const institutionMemberships = getMeta('ol-currentInstitutionsWithLicence') const personalSubscription = getMeta('ol-subscription') const managedGroupSubscriptions = getMeta('ol-managedGroupSubscriptions') const [managedInstitutions, setManagedInstitutions] = useState< ManagedInstitution[] >(getMeta('ol-managedInstitutions')) const recurlyApiKey = getMeta('ol-recurlyApiKey') const hasDisplayedSubscription = institutionMemberships?.length > 0 || personalSubscription || managedGroupSubscriptions?.length > 0 || managedInstitutions?.length > 0 useEffect(() => { if (!isRecurlyLoaded()) { setRecurlyLoadError(true) } else if (recurlyApiKey) { recurly.configure(recurlyApiKey) } }, [recurlyApiKey, setRecurlyLoadError]) useEffect(() => { if (isRecurlyLoaded() && plansWithoutDisplayPrice && personalSubscription) { const { currency, taxRate } = personalSubscription.recurly const fetchPlansDisplayPrices = async () => { for (const plan of plansWithoutDisplayPrice) { try { const priceData = await loadDisplayPriceWithTaxPromise( plan.planCode, currency, taxRate ) if (priceData?.totalForDisplay) { plan.displayPrice = priceData.totalForDisplay } } catch (error) { console.error(error) } } setPlans(plansWithoutDisplayPrice) setQueryingIndividualPlansData(false) } fetchPlansDisplayPrices().catch(console.error) } }, [personalSubscription, plansWithoutDisplayPrice]) useEffect(() => { if ( isRecurlyLoaded() && groupPlanToChangeToCode && groupPlanToChangeToSize && groupPlanToChangeToUsage && personalSubscription ) { setQueryingGroupPlanToChangeToPrice(true) const { currency, taxRate } = personalSubscription.recurly const fetchGroupDisplayPrice = async () => { setGroupPlanToChangeToPriceError(false) let priceData try { priceData = await loadGroupDisplayPriceWithTaxPromise( groupPlanToChangeToCode, currency, taxRate, groupPlanToChangeToSize, groupPlanToChangeToUsage ) } catch (e) { console.error(e) setGroupPlanToChangeToPriceError(true) } setQueryingGroupPlanToChangeToPrice(false) setGroupPlanToChangeToPrice(priceData) } fetchGroupDisplayPrice() } }, [ groupPlanToChangeToUsage, groupPlanToChangeToSize, personalSubscription, groupPlanToChangeToCode, ]) 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( () => ({ groupPlanToChangeToCode, groupPlanToChangeToPrice, groupPlanToChangeToPriceError, groupPlanToChangeToSize, groupPlanToChangeToUsage, handleCloseModal, handleOpenModal, hasDisplayedSubscription, institutionMemberships, managedGroupSubscriptions, managedInstitutions, updateManagedInstitution, modalIdShown, personalSubscription, plans, planCodeToChangeTo, queryingGroupPlanToChangeToPrice, queryingIndividualPlansData, recurlyLoadError, setGroupPlanToChangeToCode, setGroupPlanToChangeToSize, setGroupPlanToChangeToUsage, setModalIdShown, setPlanCodeToChangeTo, setRecurlyLoadError, showCancellation, setShowCancellation, showChangePersonalPlan, setShowChangePersonalPlan, }), [ groupPlanToChangeToCode, groupPlanToChangeToPrice, groupPlanToChangeToPriceError, groupPlanToChangeToSize, groupPlanToChangeToUsage, handleCloseModal, handleOpenModal, hasDisplayedSubscription, institutionMemberships, managedGroupSubscriptions, managedInstitutions, updateManagedInstitution, modalIdShown, personalSubscription, plans, planCodeToChangeTo, queryingGroupPlanToChangeToPrice, queryingIndividualPlansData, recurlyLoadError, setGroupPlanToChangeToCode, setGroupPlanToChangeToSize, setGroupPlanToChangeToUsage, setModalIdShown, setPlanCodeToChangeTo, setRecurlyLoadError, showCancellation, setShowCancellation, showChangePersonalPlan, setShowChangePersonalPlan, ] ) return ( {children} ) } export function useSubscriptionDashboardContext() { const context = useContext(SubscriptionDashboardContext) if (!context) { throw new Error( 'SubscriptionDashboardContext is only available inside SubscriptionDashboardProvider' ) } return context }