-
Notifications
You must be signed in to change notification settings - Fork 3
better bad data handling for the org dashboard #2427
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
Changes from 2 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 |
---|---|---|
|
@@ -5,7 +5,7 @@ import DOMPurify from "dompurify" | |
import Image from "next/image" | ||
import { useFeatureFlagEnabled } from "posthog-js/react" | ||
import { FeatureFlags } from "@/common/feature_flags" | ||
import { useQuery } from "@tanstack/react-query" | ||
import { useQueries, useQuery } from "@tanstack/react-query" | ||
import { | ||
programsQueries, | ||
programCollectionQueries, | ||
|
@@ -101,12 +101,81 @@ const ProgramDescription = styled(Typography)({ | |
}, | ||
}) | ||
|
||
// Custom hook to handle multiple program queries and check if any have courses | ||
const useProgramCollectionCourses = (programIds: number[], orgId: number) => { | ||
const programQueries = useQueries({ | ||
queries: programIds.map((programId) => ({ | ||
...programsQueries.programsList({ id: programId, org_id: orgId }), | ||
queryKey: [ | ||
...programsQueries.programsList({ id: programId, org_id: orgId }) | ||
.queryKey, | ||
], | ||
})), | ||
}) | ||
|
||
const isLoading = programQueries.some((query) => query.isLoading) | ||
|
||
const programsWithCourses = programQueries | ||
.map((query, index) => { | ||
if (!query.data?.results || !query.data.results.length) { | ||
return null | ||
} | ||
const program = query.data.results[0] | ||
const transformedProgram = transform.mitxonlineProgram(program) | ||
return { | ||
programId: programIds[index], | ||
program: transformedProgram, | ||
hasCourses: program.courses && program.courses.length > 0, | ||
} | ||
}) | ||
.filter(Boolean) | ||
|
||
const hasAnyCourses = programsWithCourses.some((p) => p?.hasCourses) | ||
|
||
return { | ||
isLoading, | ||
programsWithCourses, | ||
hasAnyCourses, | ||
} | ||
} | ||
|
||
const OrgProgramCollectionDisplay: React.FC<{ | ||
collection: DashboardProgramCollection | ||
enrollments?: CourseRunEnrollment[] | ||
orgId: number | ||
}> = ({ collection, enrollments, orgId }) => { | ||
const sanitizedDescription = DOMPurify.sanitize(collection.description ?? "") | ||
const { isLoading, programsWithCourses, hasAnyCourses } = | ||
useProgramCollectionCourses(collection.programIds, orgId) | ||
|
||
if (isLoading) { | ||
return ( | ||
<ProgramRoot data-testid="org-program-collection-root"> | ||
<ProgramHeader> | ||
<Typography variant="h5" component="h2"> | ||
{collection.title} | ||
</Typography> | ||
<ProgramDescription | ||
variant="body2" | ||
dangerouslySetInnerHTML={{ __html: sanitizedDescription }} | ||
/> | ||
</ProgramHeader> | ||
<PlainList> | ||
<Skeleton | ||
width="100%" | ||
height="65px" | ||
style={{ marginBottom: "16px" }} | ||
/> | ||
</PlainList> | ||
</ProgramRoot> | ||
) | ||
} | ||
|
||
// Only render if at least one program has courses | ||
if (!hasAnyCourses) { | ||
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. Are we likely to ever hit this scenario or this just the bad data defense? Thinking if it's ever expected under normal conditions it's odd to display the program title and description with load skeleton and then to disappear it once loaded with no courses (as opposed to just display e.g. "No courses are available in this program at this time"). 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. It will show the loading skeleton for the program while the API response is still coming though, but if it's determined that the program isn't returning courses, then nothing will be shown. This is mostly a bad data defense, realistically this should never happen in production. I will ask Steve / Bilal what might be expected here when I talk to them next, but just leave it as-is for now. |
||
return null | ||
} | ||
|
||
return ( | ||
<ProgramRoot data-testid="org-program-collection-root"> | ||
<ProgramHeader> | ||
|
@@ -119,14 +188,15 @@ const OrgProgramCollectionDisplay: React.FC<{ | |
/> | ||
</ProgramHeader> | ||
<PlainList> | ||
{collection.programIds.map((programId) => ( | ||
<ProgramCollectionItem | ||
key={programId} | ||
programId={programId} | ||
enrollments={enrollments} | ||
orgId={orgId} | ||
/> | ||
))} | ||
{programsWithCourses.map((item) => | ||
item ? ( | ||
<ProgramCollectionItem | ||
key={item.programId} | ||
program={item.program} | ||
enrollments={enrollments} | ||
/> | ||
) : null, | ||
)} | ||
</PlainList> | ||
</ProgramRoot> | ||
) | ||
|
@@ -182,28 +252,10 @@ const OrgProgramDisplay: React.FC<{ | |
} | ||
|
||
const ProgramCollectionItem: React.FC<{ | ||
programId: number | ||
program: DashboardProgram | ||
enrollments?: CourseRunEnrollment[] | ||
orgId: number | ||
}> = ({ programId, enrollments, orgId }) => { | ||
const program = useQuery( | ||
programsQueries.programsList({ id: programId, org_id: orgId }), | ||
) | ||
if (program.isLoading || !program.data?.results.length) { | ||
return ( | ||
<Skeleton width="100%" height="65px" style={{ marginBottom: "16px" }} /> | ||
) | ||
} | ||
const transformedProgram = transform.mitxonlineProgram( | ||
program.data?.results[0], | ||
) | ||
return ( | ||
<ProgramCard | ||
program={transformedProgram} | ||
enrollments={enrollments} | ||
orgId={orgId} | ||
/> | ||
) | ||
}> = ({ program, enrollments }) => { | ||
return <ProgramCard program={program} enrollments={enrollments} /> | ||
} | ||
|
||
const ProgramCard: React.FC<{ | ||
|
@@ -311,6 +363,13 @@ const OrganizationContentInternal: React.FC< | |
})} | ||
</PlainList> | ||
)} | ||
{programs.data?.results.length === 0 && ( | ||
<HeaderRoot> | ||
<Typography variant="h3" component="h1"> | ||
No programs found | ||
</Typography> | ||
</HeaderRoot> | ||
)} | ||
</OrganizationRoot> | ||
) | ||
} | ||
|
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.