-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated designs for the unit page (#1325)
* Design updates * Refactor out UnitCard. Apply styles for desktop * Mobile breakpoint for logo container * Migration for updated copy * Remove log line, fix migration * Apply loading styles
- Loading branch information
Showing
3 changed files
with
264 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from django.db import migrations | ||
|
||
fixtures = [ | ||
{ | ||
"name": "mitpe", | ||
"channel_configuration": { | ||
"heading": ( | ||
"Offering lifelong learning opportunities that prepare engineering, " | ||
"science, and technology professionals to address complex industry " | ||
"challenges." | ||
), | ||
}, | ||
}, | ||
] | ||
|
||
|
||
def update_copy(apps, schema_editor): | ||
Channel = apps.get_model("channels", "Channel") | ||
for fixture in fixtures: | ||
channel_configuration_updates = fixture["channel_configuration"] | ||
channel = Channel.objects.get(name=fixture["name"]) | ||
if Channel.objects.filter(name=fixture["name"]).exists(): | ||
for key, val in channel_configuration_updates.items(): | ||
channel.configuration[key] = val | ||
channel.save() | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("data_fixtures", "0004_upsert_initial_topic_data"), | ||
] | ||
|
||
operations = [ | ||
migrations.RunPython(update_copy, migrations.RunPython.noop), | ||
] |
215 changes: 215 additions & 0 deletions
215
frontends/mit-open/src/pages/UnitsListingPage/UnitCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
import React from "react" | ||
import { LearningResourceOfferorDetail, OfferedByEnum } from "api" | ||
import { Card, Skeleton, Typography, styled, theme } from "ol-components" | ||
import { useChannelDetail } from "api/hooks/channels" | ||
|
||
const CardStyled = styled(Card)({ | ||
height: "100%", | ||
}) | ||
|
||
const UnitCardContainer = styled.div({ | ||
display: "flex", | ||
flexDirection: "column", | ||
alignItems: "center", | ||
height: "100%", | ||
backgroundColor: "rgba(243, 244, 248, 0.50)", | ||
[theme.breakpoints.down("md")]: { | ||
backgroundColor: theme.custom.colors.white, | ||
}, | ||
}) | ||
|
||
const UnitCardContent = styled.div({ | ||
display: "flex", | ||
flexDirection: "column", | ||
flexGrow: 1, | ||
width: "100%", | ||
}) | ||
|
||
const LogoContainer = styled.div({ | ||
padding: "40px 32px", | ||
backgroundColor: theme.custom.colors.white, | ||
[theme.breakpoints.down("md")]: { | ||
padding: "34px 0 14px", | ||
".MuiSkeleton-root": { | ||
margin: "0 auto", | ||
}, | ||
}, | ||
}) | ||
|
||
const UnitLogo = styled.img({ | ||
height: "50px", | ||
display: "block", | ||
[theme.breakpoints.down("md")]: { | ||
height: "40px", | ||
margin: "0 auto", | ||
}, | ||
}) | ||
|
||
const CardBottom = styled.div({ | ||
padding: "24px", | ||
borderTop: `1px solid ${theme.custom.colors.lightGray2}`, | ||
display: "flex", | ||
flexGrow: 1, | ||
flexDirection: "column", | ||
gap: "24px", | ||
[theme.breakpoints.down("md")]: { | ||
padding: "16px", | ||
gap: "10px", | ||
borderTop: "none", | ||
}, | ||
}) | ||
|
||
const ValuePropContainer = styled.div({ | ||
display: "flex", | ||
flexDirection: "column", | ||
alignItems: "flex-start", | ||
justifyContent: "flex-start", | ||
flexGrow: 1, | ||
paddingBottom: "16px", | ||
}) | ||
|
||
const LoadingContent = styled.div({ | ||
padding: "24px", | ||
}) | ||
|
||
const HeadingText = styled(Typography)(({ theme }) => ({ | ||
alignSelf: "stretch", | ||
color: theme.custom.colors.darkGray2, | ||
...theme.typography.body2, | ||
[theme.breakpoints.down("md")]: { | ||
display: "none", | ||
}, | ||
})) | ||
|
||
const SubHeadingText = styled(HeadingText)(({ theme }) => ({ | ||
alignSelf: "stretch", | ||
color: theme.custom.colors.darkGray2, | ||
...theme.typography.body2, | ||
display: "none", | ||
[theme.breakpoints.down("md")]: { | ||
display: "block", | ||
}, | ||
})) | ||
|
||
const CountsTextContainer = styled.div({ | ||
display: "flex", | ||
gap: "10px", | ||
[theme.breakpoints.down("md")]: { | ||
justifyContent: "flex-end", | ||
}, | ||
}) | ||
|
||
const CountsText = styled(Typography)(({ theme }) => ({ | ||
color: theme.custom.colors.darkGray2, | ||
...theme.typography.body2, | ||
[theme.breakpoints.down("md")]: { | ||
...theme.typography.body3, | ||
color: theme.custom.colors.silverGrayDark, | ||
}, | ||
})) | ||
|
||
const unitLogos = { | ||
[OfferedByEnum.Mitx]: "/static/images/unit_logos/mitx.svg", | ||
[OfferedByEnum.Ocw]: "/static/images/unit_logos/ocw.svg", | ||
[OfferedByEnum.Bootcamps]: "/static/images/unit_logos/bootcamps.svg", | ||
[OfferedByEnum.Xpro]: "/static/images/unit_logos/xpro.svg", | ||
[OfferedByEnum.Mitpe]: "/static/images/unit_logos/mitpe.svg", | ||
[OfferedByEnum.See]: "/static/images/unit_logos/see.svg", | ||
} | ||
|
||
interface UnitCardsProps { | ||
units: LearningResourceOfferorDetail[] | undefined | ||
courseCounts: Record<string, number> | ||
programCounts: Record<string, number> | ||
} | ||
|
||
interface UnitCardProps { | ||
unit: LearningResourceOfferorDetail | ||
logo: string | ||
courseCount: number | ||
programCount: number | ||
} | ||
|
||
const UnitCard: React.FC<UnitCardProps> = (props) => { | ||
const { unit, logo, courseCount, programCount } = props | ||
const channelDetailQuery = useChannelDetail("unit", unit.code) | ||
const channelDetail = channelDetailQuery.data | ||
const unitUrl = channelDetail?.channel_url | ||
|
||
return channelDetailQuery.isLoading ? ( | ||
<UnitCardLoading /> | ||
) : ( | ||
<CardStyled href={unitUrl}> | ||
<Card.Content> | ||
<UnitCardContainer> | ||
<UnitCardContent> | ||
<LogoContainer> | ||
<UnitLogo src={logo} alt={unit.name} /> | ||
</LogoContainer> | ||
<CardBottom> | ||
<ValuePropContainer> | ||
<HeadingText> | ||
{channelDetail?.configuration?.heading} | ||
</HeadingText> | ||
<SubHeadingText> | ||
{channelDetail?.configuration?.sub_heading} | ||
</SubHeadingText> | ||
</ValuePropContainer> | ||
<CountsTextContainer> | ||
<CountsText data-testid={`course-count-${unit.code}`}> | ||
{courseCount > 0 ? `Courses: ${courseCount}` : ""} | ||
</CountsText> | ||
<CountsText data-testid={`program-count-${unit.code}`}> | ||
{programCount > 0 ? `Programs: ${programCount}` : ""} | ||
</CountsText> | ||
</CountsTextContainer> | ||
</CardBottom> | ||
</UnitCardContent> | ||
</UnitCardContainer> | ||
</Card.Content> | ||
</CardStyled> | ||
) | ||
} | ||
|
||
export const UnitCardLoading = () => { | ||
return ( | ||
<Card> | ||
<Card.Content> | ||
<UnitCardContainer> | ||
<UnitCardContent> | ||
<LogoContainer> | ||
<Skeleton variant="rectangular" width="60%" height={50} /> | ||
</LogoContainer> | ||
<LoadingContent> | ||
<Skeleton variant="text" height={100} /> | ||
</LoadingContent> | ||
</UnitCardContent> | ||
</UnitCardContainer> | ||
</Card.Content> | ||
</Card> | ||
) | ||
} | ||
|
||
export const UnitCards: React.FC<UnitCardsProps> = (props) => { | ||
const { units, courseCounts, programCounts } = props | ||
return ( | ||
<> | ||
{units?.map((unit) => { | ||
const courseCount = courseCounts[unit.code] || 0 | ||
const programCount = programCounts[unit.code] || 0 | ||
const logo = | ||
unitLogos[unit.code as OfferedByEnum] || | ||
`/static/images/unit_logos/${unit.code}.svg` | ||
return unit.value_prop ? ( | ||
<UnitCard | ||
key={unit.code} | ||
unit={unit} | ||
logo={logo} | ||
courseCount={courseCount} | ||
programCount={programCount} | ||
/> | ||
) : null | ||
})} | ||
</> | ||
) | ||
} |
Oops, something went wrong.