-
Notifications
You must be signed in to change notification settings - Fork 6
[DO_NOT_MERGE][POC] feat: academic selection step #133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,36 @@ | ||
| import { getConfig } from '@edx/frontend-platform/config'; | ||
| import { Col, Row, Stack, Stepper } from '@openedx/paragon'; | ||
| import { ReactElement, useEffect } from 'react'; | ||
|
|
||
| import { PurchaseSummary } from '@/components/PurchaseSummary'; | ||
| import { StepperTitle } from '@/components/Stepper/StepperTitle'; | ||
| import { AccountDetails, BillingDetails, PlanDetails } from '@/components/Stepper/Steps'; | ||
| import { AccountDetails, BillingDetails, EssentialsAcademicSelection, PlanDetails } from '@/components/Stepper/Steps'; | ||
| import { CheckoutSubstepKey } from '@/constants/checkout'; | ||
| import useCurrentStep from '@/hooks/useCurrentStep'; | ||
| import { isFeatureEnabled } from '@/utils/common'; | ||
|
|
||
| const Steps = (): ReactElement => ( | ||
| <> | ||
| <PlanDetails /> | ||
| <AccountDetails /> | ||
| <BillingDetails /> | ||
| </> | ||
| ); | ||
| const Steps = (): ReactElement => { | ||
| const { | ||
| FEATURE_SELF_SERVICE_ESSENTIALS, | ||
| FEATURE_SELF_SERVICE_ESSENTIALS_KEY, | ||
| } = getConfig(); | ||
| return ( | ||
| <> | ||
| { | ||
| isFeatureEnabled( | ||
| FEATURE_SELF_SERVICE_ESSENTIALS, | ||
| FEATURE_SELF_SERVICE_ESSENTIALS_KEY, | ||
| ) && <EssentialsAcademicSelection /> | ||
|
Comment on lines
+20
to
+23
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very important |
||
| } | ||
| <PlanDetails /> | ||
| <AccountDetails /> | ||
| <BillingDetails /> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| const CheckoutStepperContainer = (): ReactElement => { | ||
| const { currentStepKey, currentSubstepKey } = useCurrentStep(); | ||
|
|
||
| useEffect(() => { | ||
| const preventUnload = (e: BeforeUnloadEvent) => { | ||
| if (currentSubstepKey !== CheckoutSubstepKey.Success) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,14 @@ | ||||||
| import type { UseFormReturn } from 'react-hook-form'; | ||||||
|
|
||||||
| interface EssentialsAcademicSelectionContentProps { | ||||||
| form: UseFormReturn<EssentialAcademicSelectionData>; | ||||||
|
||||||
| form: UseFormReturn<EssentialAcademicSelectionData>; | |
| form: UseFormReturn<EssentialsAcademicSelectionData>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { EssentialsAcademicSelectionPage } from '@/components/academic-selection-page'; | ||
|
|
||
| // TODO: unnecessary layer of abstraction, just move component logic into this file. | ||
| const EssentialsAcademicSelection: React.FC = () => ( | ||
| <EssentialsAcademicSelectionPage /> | ||
| ); | ||
|
|
||
| export default EssentialsAcademicSelection; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export { default as PlanDetails } from './PlanDetails'; | ||
| export { default as AccountDetails } from './AccountDetails'; | ||
| export { default as BillingDetails } from './BillingDetails'; | ||
| export { default as EssentialsAcademicSelection } from './EssentialsAcademicSelection'; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,45 @@ | ||||||
| import { zodResolver } from '@hookform/resolvers/zod'; | ||||||
| import { Stack, Stepper } from '@openedx/paragon'; | ||||||
| import { useMemo } from 'react'; | ||||||
| import { Helmet } from 'react-helmet'; | ||||||
| import { useForm } from 'react-hook-form'; | ||||||
|
|
||||||
| import { useFormValidationConstraints } from '@/components/app/data'; | ||||||
| import { useStepperContent } from '@/components/Stepper/Steps/hooks'; | ||||||
| import { CheckoutStepKey, DataStoreKey } from '@/constants/checkout'; | ||||||
| import { useCheckoutFormStore, useCurrentPageDetails } from '@/hooks/index'; | ||||||
|
|
||||||
| const EssentialsAcademicSelectionPage = () => { | ||||||
| const StepperContent = useStepperContent(); | ||||||
| const { data: formValidationConstraints } = useFormValidationConstraints(); | ||||||
| const eventKey = CheckoutStepKey.Essentials; | ||||||
| const { | ||||||
| formSchema, | ||||||
| } = useCurrentPageDetails(); | ||||||
| const essentialsFormData = useCheckoutFormStore((state) => state.formData[DataStoreKey.EssentialsAcademicSelection]); | ||||||
|
|
||||||
| const essentialsAcademicSelectionSchema = useMemo(() => ( | ||||||
| formSchema(formValidationConstraints) | ||||||
| ), [formSchema, formValidationConstraints]); | ||||||
|
|
||||||
| const form = useForm<EssentialAcademicSelectionData>({ | ||||||
|
||||||
| const form = useForm<EssentialAcademicSelectionData>({ | |
| const form = useForm<EssentialsAcademicSelectionData>({ |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new EssentialsAcademicSelectionPage component lacks test coverage. Similar page components in the codebase (e.g., AccountDetailsPage) have corresponding test files. Consider adding tests to cover the component's rendering, form validation, and integration with the stepper content.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { default as EssentialsAcademicSelectionPage } from './EssentialsAcademicSelectionPage'; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,10 @@ import { CheckoutPageRoute, DataStoreKey } from '@/constants/checkout'; | |||||
| import { checkoutFormStore } from '@/hooks/useCheckoutFormStore'; | ||||||
| import { extractPriceId, getCheckoutPageDetails, getStepFromParams } from '@/utils/checkout'; | ||||||
|
|
||||||
| async function essentialsAcademicSelectionLoader(): Promise<Response | null> { | ||||||
|
||||||
| async function essentialsAcademicSelectionLoader(): Promise<Response | null> { | |
| async function essentialsAcademicSelectionLoader(_queryClient: QueryClient): Promise<Response | null> { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ import { | |
| } from '@/components/app/routes/loaders/utils'; | ||
| import { CheckoutPageRoute, EssentialsPageRoute } from '@/constants/checkout'; | ||
| import { extractPriceId } from '@/utils/checkout'; | ||
| import { isFeatureEnabled } from '@/utils/common'; | ||
|
|
||
| /** | ||
| * Factory that creates the root route loader for the Enterprise Checkout MFE. | ||
|
|
@@ -87,14 +88,9 @@ const makeRootLoader = ( | |
|
|
||
| // Feature flag check | ||
| if (routeFeatureKey) { | ||
| const sessionKey = sessionStorage.getItem(SSP_SESSION_KEY); | ||
| const isUnlocked = isFeatureEnabled(false, routeFeatureKey); | ||
|
|
||
| const isUnlockedBySiteKey = !!FEATURE_SELF_SERVICE_SITE_KEY | ||
| && sessionKey === FEATURE_SELF_SERVICE_SITE_KEY; | ||
|
|
||
| const isUnlockedByRouteKey = sessionKey === routeFeatureKey; | ||
|
|
||
| if (!isUnlockedBySiteKey && !isUnlockedByRouteKey) { | ||
| if (!isUnlocked) { | ||
| const featureParam = new URL(request.url).searchParams.get('feature'); | ||
|
|
||
| const paramIsSiteKey = !!FEATURE_SELF_SERVICE_SITE_KEY | ||
|
|
@@ -121,11 +117,11 @@ const makeRootLoader = ( | |
| * Essentials routes do not participate in checkout intent logic. | ||
| * This check happens AFTER feature flag validation. | ||
| */ | ||
| const isCheckoutRoute = !Object.values(EssentialsPageRoute).some(route => isPathMatch(currentPath, route)); | ||
|
|
||
| if (!isCheckoutRoute) { | ||
| return null; | ||
| } | ||
| // const isCheckoutRoute = !Object.values(EssentialsPageRoute).some(route => isPathMatch(currentPath, route)); | ||
| // | ||
| // if (!isCheckoutRoute) { | ||
| // return null; | ||
| // } | ||
|
Comment on lines
+120
to
+124
|
||
|
|
||
| // Fetch basic info about authenticated user from JWT token, and also hydrate it with additional | ||
| // information from the `<LMS>/api/user/v1/accounts/<username>` endpoint. We need access to the | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove console.log statement before merging. This debug output should not be committed to production code.