Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {
} from "./DashboardDialogs"
import NiceModal from "@ebay/nice-modal-react"
import { useCreateEnrollment } from "api/mitxonline-hooks/enrollment"
import { useMitxOnlineUserMe } from "api/mitxonline-hooks/user"
import { mitxUserQueries } from "api/mitxonline-hooks/user"
import { useQuery } from "@tanstack/react-query"

const CardRoot = styled.div<{
screenSize: "desktop" | "mobile"
Expand Down Expand Up @@ -66,10 +67,6 @@ const CardRoot = styled.div<{
},
])

const SpinnerContainer = styled.div({
marginLeft: "8px",
})

const TitleLink = styled(Link)(({ theme }) => ({
[theme.breakpoints.down("md")]: {
maxWidth: "calc(100% - 16px)",
Expand Down Expand Up @@ -117,6 +114,40 @@ const getDefaultContextMenuItems = (
]
}

const useOneClickEnroll = () => {
const mitxOnlineUser = useQuery(mitxUserQueries.me())
const createEnrollment = useCreateEnrollment()
const userCountry = mitxOnlineUser.data?.legal_address?.country
const userYearOfBirth = mitxOnlineUser.data?.user_profile?.year_of_birth
const showJustInTimeDialog = !userCountry || !userYearOfBirth

const mutate = ({
href,
coursewareId,
}: {
href: string
coursewareId: string
}) => {
if (showJustInTimeDialog) {
NiceModal.show(JustInTimeDialog, {
href: href,
readableId: coursewareId,
})
return
} else {
createEnrollment.mutate(
{ readable_id: coursewareId },
{
onSuccess: () => {
window.location.assign(href)
},
},
)
}
}
return { mutate, isPending: createEnrollment.isPending }
}

type CoursewareButtonProps = {
coursewareId?: string | null
startDate?: string | null
Expand Down Expand Up @@ -161,62 +192,53 @@ const CoursewareButton = styled(
courseNoun,
enrollmentStatus,
})
const mitxOnlineUser = useMitxOnlineUserMe()
const hasStarted = startDate && isInPast(startDate)
const hasEnrolled =
enrollmentStatus && enrollmentStatus !== EnrollmentStatus.NotEnrolled
const createEnrollment = useCreateEnrollment()
const userCountry = mitxOnlineUser.data?.legal_address?.country
const userYearOfBirth = mitxOnlineUser.data?.user_profile?.year_of_birth
const showJustInTimeDialog = !userCountry || !userYearOfBirth
return (hasStarted && href) || !hasEnrolled ? (
hasEnrolled && href ? (
<ButtonLink

const oneClickEnroll = useOneClickEnroll()

if (!hasEnrolled /* enrollment flow */) {
return (
<Button
size="small"
variant="primary"
endIcon={<RiArrowRightLine />}
href={href}
className={className}
disabled={oneClickEnroll.isPending || !coursewareId}
onClick={() => {
if (!href || !coursewareId) return
oneClickEnroll.mutate({ href, coursewareId })
}}
endIcon={
oneClickEnroll.isPending ? (
<LoadingSpinner
color="inherit"
loading={oneClickEnroll.isPending}
size={16}
/>
) : undefined
}
{...others}
>
{coursewareText}
</ButtonLink>
) : (
<Button
</Button>
)
} else if (hasStarted && href /* Link to course */) {
return (
<ButtonLink
size="small"
variant="primary"
endIcon={<RiArrowRightLine />}
href={href}
className={className}
disabled={createEnrollment.isPending || !coursewareId}
onClick={async () => {
if (!href || !coursewareId) return
if (showJustInTimeDialog) {
NiceModal.show(JustInTimeDialog, {
href: href,
readableId: coursewareId,
})
return
} else {
await createEnrollment.mutateAsync(
{ readable_id: coursewareId },
{
onSuccess: () => {
window.location.href = href
},
},
)
}
}}
{...others}
>
{coursewareText}
{createEnrollment.isPending && (
<SpinnerContainer>
<LoadingSpinner loading={createEnrollment.isPending} size={16} />
</SpinnerContainer>
)}
</Button>
</ButtonLink>
)
) : (
}
// Disabled
return (
<Button
size="small"
variant="primary"
Expand Down Expand Up @@ -336,14 +358,14 @@ const CourseStartCountdown: React.FC<{

type DashboardCardProps = {
Component?: React.ElementType
titleAction: "marketing" | "courseware"
dashboardResource: DashboardResource
showNotComplete?: boolean
className?: string
courseNoun?: string
offerUpgrade?: boolean
contextMenuItems?: SimpleMenuItem[]
isLoading?: boolean
titleHref?: string | null
buttonHref?: string | null
}

Expand All @@ -356,11 +378,29 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
offerUpgrade = true,
contextMenuItems = [],
isLoading = false,
titleHref,
buttonHref,
titleAction,
}) => {
const course = dashboardResource as DashboardCourse
const { title, marketingUrl, enrollment, run } = course
const oneClickEnroll = useOneClickEnroll()

const coursewareUrl = run.coursewareUrl
const titleHref =
titleAction === "marketing" ? marketingUrl : (coursewareUrl ?? marketingUrl)
const hasEnrolled =
enrollment?.status && enrollment.status !== EnrollmentStatus.NotEnrolled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticing in the Django admin we have

Image

and on the DashboardCourse -

const EnrollmentStatus = {
  NotEnrolled: "not_enrolled",
  Enrolled: "enrolled",
  Completed: "completed",
} as const

Unenrolling in the dashboard sets the status to unenrolled, but setting values in the admin has no effect. Not sure if this is of consequence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I .... do not know what the CourseRunEnrollment status in the admin panel is for :/

const titleClick: React.MouseEventHandler | undefined =
titleAction === "courseware" && coursewareUrl && !hasEnrolled
? (e) => {
e.preventDefault()
if (!course.coursewareId) return
oneClickEnroll.mutate({
href: coursewareUrl,
coursewareId: course.coursewareId,
})
}
: undefined

const titleSection = isLoading ? (
<>
Expand All @@ -373,7 +413,8 @@ const DashboardCard: React.FC<DashboardCardProps> = ({
<TitleLink
size="medium"
color="black"
href={titleHref ? titleHref : marketingUrl}
href={titleHref}
onClick={titleClick}
>
{title}
</TitleLink>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ describe("JustInTimeDialog", () => {
test("Opens just-in-time dialog when enrolling with incomplete mitxonline user data", async () => {
const { course } = setupJustInTimeTest()

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)

const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
Expand Down Expand Up @@ -250,7 +252,9 @@ describe("JustInTimeDialog", () => {
"Dialog pre-populates with user data if available",
async ({ userOverrides, expectCountry, expectYob }) => {
const { course } = setupJustInTimeTest({ userOverrides })
renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)
const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
const dialog = await screen.findByRole("dialog", {
Expand All @@ -265,7 +269,9 @@ describe("JustInTimeDialog", () => {
test("Validates required fields in just-in-time dialog", async () => {
const { course } = setupJustInTimeTest()

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)

const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
Expand Down Expand Up @@ -296,7 +302,9 @@ describe("JustInTimeDialog", () => {
test("Generates correct year of birth options (minimum age 13)", async () => {
const { course } = setupJustInTimeTest()

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)

const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
Expand All @@ -321,7 +329,9 @@ describe("JustInTimeDialog", () => {
test("Shows expected countries in country dropdown", async () => {
const { course, countries } = setupJustInTimeTest()

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)

const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
Expand All @@ -344,7 +354,9 @@ describe("JustInTimeDialog", () => {
test("Cancels just-in-time dialog without making API calls", async () => {
const { course } = setupJustInTimeTest()

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)

const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button
Expand Down Expand Up @@ -372,7 +384,9 @@ describe("JustInTimeDialog", () => {
userOverrides: { user_profile: { year_of_birth: 1988 } },
})

renderWithProviders(<DashboardCard dashboardResource={course} />)
renderWithProviders(
<DashboardCard titleAction="marketing" dashboardResource={course} />,
)
const enrollButtons = await screen.findAllByTestId("courseware-button")
await user.click(enrollButtons[0]) // Use the first (desktop) button

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ const BoldText = styled.span(({ theme }) => ({
...theme.typography.subtitle1,
}))

const SpinnerContainer = styled.div({
marginLeft: "8px",
})

const SelectPlaceholder = styled("span")(({ theme }) => ({
color: theme.custom.colors.silverGrayDark,
}))
Expand Down Expand Up @@ -88,17 +84,13 @@ const EmailSettingsDialogInner: React.FC<DashboardDialogProps> = ({
variant="primary"
type="submit"
disabled={!formik.dirty || updateEnrollment.isPending}
endIcon={
updateEnrollment.isPending ? (
<LoadingSpinner color="inherit" loading={true} size={16} />
) : undefined
}
>
Save Settings
{updateEnrollment.isPending && (
<SpinnerContainer>
<LoadingSpinner
color="inherit"
loading={updateEnrollment.isPending}
size={16}
/>
</SpinnerContainer>
)}
</Button>
</DialogActions>
}
Expand Down Expand Up @@ -169,17 +161,13 @@ const UnenrollDialogInner: React.FC<DashboardDialogProps> = ({
variant="primary"
type="submit"
disabled={destroyEnrollment.isPending}
endIcon={
destroyEnrollment.isPending ? (
<LoadingSpinner color="inherit" loading={true} size={16} />
) : undefined
}
>
Unenroll
{destroyEnrollment.isPending && (
<SpinnerContainer>
<LoadingSpinner
loading={destroyEnrollment.isPending}
color="inherit"
size={16}
/>
</SpinnerContainer>
)}
</Button>
</DialogActions>
}
Expand Down Expand Up @@ -275,13 +263,13 @@ const JustInTimeDialogInner: React.FC<{ href: string; readableId: string }> = ({
variant="primary"
type="submit"
disabled={formik.isSubmitting}
endIcon={
formik.isSubmitting ? (
<LoadingSpinner color="inherit" loading={true} size={16} />
) : undefined
}
>
Submit
{formik.isSubmitting && (
<SpinnerContainer>
<LoadingSpinner color="inherit" loading={true} size={16} />
</SpinnerContainer>
)}
</Button>
</DialogActions>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const EnrollmentExpandCollapse: React.FC<EnrollmentExpandCollapseProps> = ({
<EnrollmentsList itemSpacing={"16px"}>
{shownEnrollments.map((course) => (
<DashboardCardStyled
titleAction="marketing"
key={course.key}
Component="li"
dashboardResource={course}
Expand All @@ -153,6 +154,7 @@ const EnrollmentExpandCollapse: React.FC<EnrollmentExpandCollapseProps> = ({
<HiddenEnrollmentsList itemSpacing={"16px"}>
{hiddenEnrollments.map((course) => (
<DashboardCardStyled
titleAction="marketing"
key={course.key}
Component="li"
dashboardResource={course}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ const OrgProgramCollectionDisplay: React.FC<{
dashboardResource={course}
courseNoun="Module"
offerUpgrade={false}
titleHref={course.run?.coursewareUrl}
titleAction="courseware"
buttonHref={course.run?.coursewareUrl}
/>
))}
Expand Down Expand Up @@ -326,7 +326,7 @@ const OrgProgramDisplay: React.FC<{
dashboardResource={course}
courseNoun="Module"
offerUpgrade={false}
titleHref={course.run?.coursewareUrl}
titleAction="courseware"
buttonHref={course.run?.coursewareUrl}
/>
))}
Expand Down
Loading
Loading