Skip to content

Commit 67a03f1

Browse files
authored
Merge pull request #1475 from isaacphysics/redesign/subject-overview-cleanup
Subject Overview Page cleanup
2 parents bc01d73 + f45bcc2 commit 67a03f1

File tree

5 files changed

+96
-16
lines changed

5 files changed

+96
-16
lines changed

src/app/components/elements/list-groups/AbstractListViewItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export const AbstractListViewItem = ({icon, title, subject, subtitle, breadcrumb
103103
fullWidth = fullWidth || below["sm"](deviceSize) || ((status || audienceViews || previewQuizUrl || quizButton) ? false : true);
104104
const cardBody =
105105
<div className="w-100 d-flex flex-row">
106-
<Col className={classNames("d-flex flex-grow-1", {"mt-3": isCard && linkTags?.length, "mb-3": isCard && !linkTags?.length})}>
106+
<Col className={classNames("d-flex flex-grow-1", {"mt-3": isCard, "mb-3": isCard && !linkTags?.length})}>
107107
<div className="position-relative">
108108
{icon && (
109109
icon.type === "img" ? <img src={icon.icon} alt="" className="me-3"/>

src/app/components/elements/svg/DifficultyIcons.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {difficultyLabelMap, difficultyShortLabelMap, isAda, isPhy, siteSpecific}
55
import classnames from "classnames";
66
import {Rectangle} from "./Rectangle";
77
import {Circle} from "./Circle";
8+
import classNames from "classnames";
89

910
// Difficulty icon proportions
1011
const difficultyIconWidth = siteSpecific(15, 25);
@@ -13,6 +14,8 @@ const yPadding = 2;
1314
const difficultyCategoryLevels = siteSpecific([1, 2, 3], [1, 2]);
1415
const miniHexagon = calculateHexagonProportions(difficultyIconWidth / 2, 0);
1516
const miniSquare = {width: difficultyIconWidth, height: difficultyIconWidth};
17+
const largeHexagon = calculateHexagonProportions(difficultyIconWidth, 0);
18+
const largeSquare = {width: difficultyIconWidth * 2, height: difficultyIconWidth * 2};
1619

1720
const squareOffset = ((miniHexagon.quarterHeight * 4 + 2 * yPadding) - difficultyIconWidth) / 2 - 1; // ${yPadding + (difficultyCategory === "P" && isPhy ? 0 : 2)}
1821

@@ -21,16 +24,19 @@ interface DifficultyIconShapeProps {
2124
difficultyCategoryLevel: number;
2225
active: boolean;
2326
blank?: boolean;
27+
size?: "sm" | "lg";
2428
}
25-
function SingleDifficultyIconShape({difficultyCategory, difficultyCategoryLevel, active, blank}: DifficultyIconShapeProps) {
29+
30+
function SingleDifficultyIconShape({difficultyCategory, difficultyCategoryLevel, active, blank, size}: DifficultyIconShapeProps) {
31+
const iconWidth = size === "lg" ? difficultyIconWidth * 2 : difficultyIconWidth;
2632
// FIXME the calculations here need refactoring, had to rush them to get it done
27-
return <g transform={`translate(${(difficultyCategoryLevel - 1) * (difficultyIconWidth + 2 * difficultyIconXPadding) + siteSpecific(0, 1)}, ${isAda ? yPadding + 2 : difficultyCategory === "P" ? 0 : squareOffset})`}>
33+
return <g transform={`translate(${(difficultyCategoryLevel - 1) * (iconWidth + 2 * difficultyIconXPadding) + siteSpecific(0, 1)}, ${isAda ? yPadding + 2 : difficultyCategory === "P" ? 0 : squareOffset})`}>
2834
{difficultyCategory === "P" ?
2935
siteSpecific(
30-
<Hexagon {...miniHexagon} className={"hex difficulty practice " + classnames({active})} />,
31-
<Circle radius={difficultyIconWidth / 2} className={"hex difficulty practice " + classnames({active})} />
36+
<Hexagon {...(size === "lg" ? largeHexagon : miniHexagon)} className={"hex difficulty practice " + classnames({active})} />,
37+
<Circle radius={iconWidth / 2} className={"hex difficulty practice " + classnames({active})} />
3238
) :
33-
<Rectangle {...miniSquare} className={"square difficulty challenge " + classnames({active})} />
39+
<Rectangle {...(size === "lg" ? largeSquare : miniSquare)} className={"square difficulty challenge " + classnames({active})} />
3440
}
3541
{/* {<foreignObject width={difficultyIconWidth} height={difficultyIconWidth + (difficultyCategory === "P" && isPhy ? yPadding + 2 : siteSpecific(0, 1))}>
3642
<div aria-hidden={"true"} className={`difficulty-title difficulty-icon-title ${classnames({active})} difficulty-${difficultyCategoryLevel}`}>
@@ -64,3 +70,20 @@ export function DifficultyIcons({difficulty, blank, className} : {difficulty: Di
6470
</svg>
6571
</div>;
6672
}
73+
74+
export function DifficultyIcon({difficultyCategory, className} : {difficultyCategory: string, className?: string}) {
75+
return <div className={classNames(className, "d-inline-flex ps-1 pe-1")}>
76+
<svg
77+
className="d-flex"
78+
role={"img"}
79+
width={`${difficultyIconWidth * 2}px`}
80+
height={`${largeHexagon.quarterHeight * 4 + 2 * yPadding}px`}
81+
{...(isPhy && {viewBox: `0 0 ${difficultyIconWidth * 2} ${largeHexagon.quarterHeight * 4}`})}
82+
transform="translate(0,5)"
83+
>
84+
<SingleDifficultyIconShape
85+
difficultyCategoryLevel={1} active difficultyCategory={difficultyCategory} size="lg"
86+
/>
87+
</svg>
88+
</div>;
89+
}

src/app/components/pages/SubjectLandingPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const LandingPageFooter = ({context}: {context: PageContextState}) => {
9393
// TODO: are we going to make subject-specific news?
9494
const {data: news} = useGetNewsPodListQuery({subject: "physics"});
9595

96-
return <Row className={classNames("mt-5 py-4 row-cols-1 row-cols-md-2")}>
96+
return <Row className={classNames("mt-2 py-4 row-cols-1 row-cols-md-2")}>
9797
<div className="d-flex flex-column mt-3">
9898
{/* if there are books, display books. otherwise, display news */}
9999
{books.length > 0

src/app/components/pages/SubjectOverviewPage.tsx

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ import { RouteComponentProps, withRouter } from "react-router";
33
import { Container } from "reactstrap";
44
import { TitleAndBreadcrumb } from "../elements/TitleAndBreadcrumb";
55
import { useUrlPageTheme } from "../../services/pageContext";
6-
import { HUMAN_SUBJECTS, isDefined, LEARNING_STAGE, LearningStage, PHY_NAV_SUBJECTS, Subject } from "../../services";
7-
import { PageContextState } from "../../../IsaacAppTypes";
8-
import { ListViewCards } from "../elements/list-groups/ListView";
6+
import { above, HUMAN_SUBJECTS, isDefined, LEARNING_STAGE, LearningStage, PHY_NAV_SUBJECTS, SEARCH_RESULT_TYPE, Subject, useDeviceSize } from "../../services";
7+
import { PageContextState, ShortcutResponse } from "../../../IsaacAppTypes";
8+
import { ListView, ListViewCardProps, ListViewCards } from "../elements/list-groups/ListView";
99
import { LandingPageFooter } from "./SubjectLandingPage";
10+
import { DifficultyIcon } from "../elements/svg/DifficultyIcons";
11+
import { AbstractListViewItemState } from "../elements/list-groups/AbstractListViewItem";
1012

1113
const SubjectCards = ({context}: { context: PageContextState }) => {
14+
const deviceSize = useDeviceSize();
15+
1216
if (!isDefined(context?.subject)) return null;
1317

1418
const humanSubject = context?.subject && HUMAN_SUBJECTS[context.subject];
1519

16-
return <ListViewCards showBlanks cards={[
20+
const cards: (ListViewCardProps | null)[] = [
1721
{
1822
item: {
1923
title: "11-14",
@@ -25,6 +29,7 @@ const SubjectCards = ({context}: { context: PageContextState }) => {
2529
},
2630
url: `/${context.subject}/11_14`,
2731
stage: LEARNING_STAGE["11_TO_14"],
32+
subject: context.subject,
2833
},
2934
{
3035
item: {
@@ -37,6 +42,8 @@ const SubjectCards = ({context}: { context: PageContextState }) => {
3742
},
3843
url: `/${context.subject}/gcse`,
3944
stage: LEARNING_STAGE.GCSE,
45+
subject: context.subject,
46+
state: context.subject === "biology" ? AbstractListViewItemState.COMING_SOON : undefined,
4047
},
4148
{
4249
item: {
@@ -49,6 +56,7 @@ const SubjectCards = ({context}: { context: PageContextState }) => {
4956
},
5057
url: `/${context.subject}/a_level`,
5158
stage: LEARNING_STAGE.A_LEVEL,
59+
subject: context.subject,
5260
},
5361
{
5462
item: {
@@ -61,14 +69,43 @@ const SubjectCards = ({context}: { context: PageContextState }) => {
6169
},
6270
url: `/${context.subject}/university`,
6371
stage: LEARNING_STAGE.UNIVERSITY,
64-
}
65-
]
66-
.map(({stage, ...card}) => (PHY_NAV_SUBJECTS[context.subject as Subject] as readonly LearningStage[])?.includes(stage) ? card : null)
67-
.filter((x, i, a) => x || (i % 2 === 0 ? a[i + 1] : a[i - 1])) // remove pairs of nulls
72+
subject: context.subject,
73+
},
74+
].map(({stage, ...card}) => (PHY_NAV_SUBJECTS[context.subject as Subject] as readonly LearningStage[])?.includes(stage) || card.state === AbstractListViewItemState.COMING_SOON ? card : null);
75+
76+
return <ListViewCards showBlanks={above["lg"](deviceSize)} cards={cards
6877
.sort((a, b) => a ? (b ? 0 : -1) : 1) // put nulls at the end
78+
.filter((x, i, a) => x || (i % 2 === 0 ? a[i + 1] : a[i - 1])) // remove pairs of nulls
6979
} />;
7080
};
7181

82+
const ExampleQuestions = ({ subject, className }: { subject: Subject, className: string }) => {
83+
const items: { [key in Subject]: ShortcutResponse[] } = {
84+
maths: [{
85+
title: "Sample Maths Questions",
86+
type: SEARCH_RESULT_TYPE.GAMEBOARD,
87+
id: "sample_maths_questions",
88+
}],
89+
physics: [/*{
90+
title: "Sample Physics Questions",
91+
type: SEARCH_RESULT_TYPE.GAMEBOARD,
92+
id: "sample_phy_questions",
93+
}*/], // Uncomment when physics questions are available
94+
chemistry: [{
95+
title: "Sample Chemistry Questions",
96+
type: SEARCH_RESULT_TYPE.GAMEBOARD,
97+
id: "sample_chem_questions",
98+
}],
99+
biology: [{
100+
title: "Sample Biology Questions",
101+
type: SEARCH_RESULT_TYPE.GAMEBOARD,
102+
id: "sample_bio_questions",
103+
}],
104+
};
105+
106+
return items[subject].length > 0 ? <ListView className={className} items={items[subject]} /> : null;
107+
};
108+
72109
export const SubjectOverviewPage = withRouter((props: RouteComponentProps) => {
73110
const pageContext = useUrlPageTheme();
74111

@@ -112,15 +149,30 @@ export const SubjectOverviewPage = withRouter((props: RouteComponentProps) => {
112149

113150
<p className="mt-3">
114151
All Isaac Science questions are classed as either &quot;Practice&quot; or &quot;Challenge&quot; – indicated by the symbols below.
152+
</p>
153+
154+
<div className="d-flex flex-row w-100 justify-content-center">
155+
<div className="d-flex flex-column me-3 align-items-center">
156+
<DifficultyIcon difficultyCategory="P"/>
157+
<span>Practice</span>
158+
</div>
159+
<div className="d-flex flex-column align-items-center">
160+
<DifficultyIcon difficultyCategory="C"/>
161+
<span>Challenge</span>
162+
</div>
163+
</div>
115164

165+
<p className="mt-3">
116166
In Isaac {humanSubject},
117167
<ul>
118168
<li>Practice questions are those that require one concept or equation to solve.</li>
119169
<li>Challenge questions are those that require one or more concepts, or require more creativity to solve the problem, helping to develop important problem solving skills. </li>
120170
</ul>
121171
</p>
122172

123-
{/* <ExampleQuestions/> */}
173+
<div className="d-flex justify-content-center mt-4">
174+
<ExampleQuestions className="w-100 w-md-75" subject={pageContext.subject} />
175+
</div>
124176

125177
<LandingPageFooter context={pageContext} />
126178
</div>}

src/scss/phy/list-groups.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,9 @@
147147
margin-left: 0.5rem;
148148
}
149149
}
150+
151+
img {
152+
// For items that use images rather than icons (e.g. subject logos), to remove the need to include a seperate greyscale image
153+
filter: grayscale(100%);
154+
}
150155
}

0 commit comments

Comments
 (0)