Skip to content

Commit f44ea0f

Browse files
authored
Merge pull request #1292 from isaacphysics/redesign/homepage
Implement new homepage design
2 parents dec000f + 3431f63 commit f44ea0f

File tree

8 files changed

+265
-117
lines changed

8 files changed

+265
-117
lines changed
Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Loading
76 KB
Loading

src/app/components/elements/cards/EventCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const PhysicsEventCard = ({event}: {event: AugmentedEvent}) => {
3434
</div>}
3535
</a>}
3636
<CardBody className="d-flex flex-column ps-0">
37-
{title && <CardTitle tag="h4" className="mb-0">{title}</CardTitle>}
37+
{title && <CardTitle className="mb-0 pod-title">{title}</CardTitle>}
3838
<CardText className="mb-0">
3939
{subtitle && <p className="m-0">{subtitle}</p>}
4040
</CardText>

src/app/components/elements/cards/NewsCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import {Card, CardBody, CardImg, CardProps, CardText, CardTitle} from "reactstra
44
import {IsaacPodDTO} from "../../../../IsaacApiTypes";
55
import {apiHelper, siteSpecific} from "../../../services";
66
import {AdaCard} from "./AdaCard";
7-
import { Spacer } from "../Spacer";
87
import classNames from "classnames";
8+
import { Spacer } from "../Spacer";
99

1010
interface NewsCardProps extends CardProps {
1111
newsItem: IsaacPodDTO;
@@ -24,7 +24,7 @@ const PhysicsNewsCard = ({newsItem, ...props}: NewsCardProps) => {
2424
/>
2525
</a>}
2626
<CardBody className="d-flex flex-column ps-0">
27-
<CardTitle tag="h4" className="mb-0">{title}</CardTitle>
27+
<CardTitle className="mb-0 pod-title">{title}</CardTitle>
2828
<CardText>
2929
{value && <p>{value}</p>}
3030
</CardText>
Lines changed: 159 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,173 @@
11
import React, {useEffect} from "react";
2-
import {selectors, useAppSelector, useGetNewsPodListQuery} from "../../../state";
2+
import {selectors, useAppSelector, useGetNewsPodListQuery, useLazyGetEventsQuery} from "../../../state";
33
import {Link} from "react-router-dom";
4-
import {Button, Col, Container, Row} from "reactstrap";
5-
import {NewsCarousel} from "../../elements/NewsCarousel";
6-
import {above, SITE_TITLE, useDeviceSize, useUserConsent} from "../../../services";
7-
import {HomepageYoutubeCookieHandler} from "../../handlers/InterstitialCookieHandler";
4+
import {Button, Card, CardBody, CardProps, CardText, CardTitle, Col, Container, Row} from "reactstrap";
5+
import {above, EventStatusFilter, EventTypeFilter, HUMAN_STAGES, HUMAN_SUBJECTS, isLoggedIn, PHY_NAV_SUBJECTS, SITE_TITLE, STAGE, useDeviceSize} from "../../../services";
6+
import { NewsCard } from "../../elements/cards/NewsCard";
7+
import { ShowLoadingQuery } from "../../handlers/ShowLoadingQuery";
8+
import { EventCard } from "../../elements/cards/EventCard";
89
import { StudentDashboard } from "../../elements/StudentDashboard";
10+
import { Subject, ShortcutResponse } from "../../../../IsaacAppTypes";
11+
import { ListViewCardProps, ListViewCards } from "../../elements/list-groups/ListView";
12+
import { Spacer } from "../../elements/Spacer";
13+
14+
interface HomepageHeroCardProps extends CardProps {
15+
title?: string;
16+
content?: string;
17+
isStudent?: boolean;
18+
}
19+
20+
const HomepageHeroCard = ({title, content, isStudent}: HomepageHeroCardProps) => {
21+
return <Card className="homepage-hero-card border-0">
22+
<CardBody className="p-4 pt-5 d-flex flex-column">
23+
<div className="homepage-hero-card-tag">
24+
{isStudent
25+
? <>
26+
<img src="/assets/phy/icons/redesign/homepage-hero-student-flag.svg" height={"40px"} alt={"student card tag"}/>
27+
<b>STUDENTS</b>
28+
</>
29+
: <>
30+
<img src="/assets/phy/icons/redesign/homepage-hero-teacher-flag.svg" height={"40px"} alt={"teacher card tag"}/>
31+
<b>TEACHERS</b>
32+
</>
33+
}
34+
</div>
35+
<CardTitle className="mt-1" tag="h4">{title}</CardTitle>
36+
<CardText>{content}</CardText>
37+
<Spacer/>
38+
<Button className="w-max-content" tag={Link} to={isStudent ? "/login" : "/teacher_account_request"}>Create a {isStudent ? "student" : "teacher"} account</Button>
39+
</CardBody>
40+
</Card>;
41+
};
42+
43+
const HomepageHero = () => {
44+
const user = useAppSelector(selectors.user.orNull);
45+
const deviceSize = useDeviceSize();
46+
if (!isLoggedIn(user)) {
47+
return <div className="homepage-hero">
48+
{above['md'](deviceSize) && <div className="homepage-hero-img"/>}
49+
<Container className="pt-5">
50+
<div className="w-100 w-md-50 mb-4 mb-md-5 mb-xl-6 pe-xl-5">
51+
<div className="physics-strapline mb-3">
52+
<h2><span className="text-green">Master Science subjects</span> by solving problems</h2>
53+
</div>
54+
From School to University – <b>Isaac</b> is a free platform for teachers and students for use in the classroom, for homework and for revision.
55+
</div>
56+
{!above['md'](deviceSize) && <div className="homepage-hero-img container-override"/>}
57+
<Row className="row-cols-1 row-cols-md-2">
58+
<Col className="mb-3">
59+
<HomepageHeroCard
60+
title="Build Confidence in Physics through Practice"
61+
content="Tackle interactive questions, explore varying difficulty levels, and strengthen problem-solving skills with concept guides, video lessons, and events."
62+
isStudent={true}/>
63+
</Col>
64+
<Col className="mb-3">
65+
<HomepageHeroCard
66+
title="Support students in developing skills and achieving higher results"
67+
content="Assign, track, and manage student progress with ease—ideal for classwork, homework, or revision. Trusted by over 1,000 UK schools."
68+
isStudent={false}/>
69+
</Col>
70+
</Row>
71+
</Container>
72+
</div>;
73+
}
74+
};
75+
76+
type subjectCategory = {subject: string, humanSubject: string, subcategories: {humanStage: string, href: string}[]};
77+
const subjectCategories = Object.entries(PHY_NAV_SUBJECTS).map(([subject, stages]) => {
78+
return {
79+
subject: subject,
80+
humanSubject: HUMAN_SUBJECTS[subject],
81+
subcategories: stages.map((stage) => {
82+
return {
83+
humanStage: HUMAN_STAGES[stage.valueOf()],
84+
href: `/${subject}/${stage}`,
85+
};
86+
})
87+
};
88+
});
89+
90+
const getListViewSubjectCard = (sc: subjectCategory) => {
91+
const item: ShortcutResponse = {
92+
title: sc.humanSubject,
93+
subtitle: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris faucibus est vulputate augue tristique, sed vehicula turpis pellentesque.",
94+
};
95+
96+
const listViewSubjectCard: ListViewCardProps = {
97+
item: item,
98+
icon: {type: "img", icon: `/assets/phy/icons/redesign/subject-${sc.subject}.svg`},
99+
subject: sc.subject as Subject,
100+
linkTags: sc.subcategories.map((subcat) => ({tag: subcat.humanStage, url: subcat.href}))
101+
};
102+
103+
return listViewSubjectCard;
104+
};
105+
106+
const cards = subjectCategories.map((sc) => getListViewSubjectCard(sc));
9107

10108
export const HomepagePhy = () => {
11109
useEffect( () => {document.title = SITE_TITLE;}, []);
12-
const {data: news} = useGetNewsPodListQuery({subject: "physics"});
110+
13111
const user = useAppSelector(selectors.user.orNull);
14-
const deviceSize = useDeviceSize();
15-
const userConsent = useUserConsent();
112+
const {data: news} = useGetNewsPodListQuery({subject: "physics"});
16113

114+
const [getEventsList, eventsQuery] = useLazyGetEventsQuery();
115+
useEffect(() => {
116+
getEventsList({startIndex: 0, limit: 2, typeFilter: EventTypeFilter["All events"], statusFilter: EventStatusFilter["Upcoming events"], stageFilter: [STAGE.ALL]});
117+
}, []);
118+
17119
return <>
18-
{/*<WarningBanner/>*/}
19-
<StudentDashboard />
20-
<div id="homepage" className="pb-5 px-2 px-sm-5 mx-md-5 px-lg-0">
21-
<section id="call-to-action" className="homepageHero">
22-
<Container className="pt-4">
23-
<Row className="mt-sm-4">
24-
<Col lg={5} className="physics-site-intro">
25-
<h1 className={`physics-strapline ${above["lg"](deviceSize) ? "h2" : ""} mb-lg-3`}>
26-
{above["sm"](deviceSize) ?
27-
<>Master Physics by Solving Problems:<br /><small>from School to University!</small></> :
28-
<>Master Physics by Solving Problems</>}
29-
</h1>
30-
<p>Welcome to Isaac Physics, the free platform for teachers and students.</p>
31-
<ul>
32-
<li>Use it in the <strong>classroom</strong></li>
33-
<li>Use it for <strong>homework</strong></li>
34-
<li>Use it for <strong>revision</strong></li>
35-
</ul>
36-
</Col>
37-
<Col lg={7} className={above["lg"](deviceSize) ? `align-items-stretch d-flex flex-column` : ""}>
38-
{!(user && user.loggedIn) && <Row className="align-self-end mt-2 mt-lg-0 mb-1 mb-lg-0">
39-
<Col className="col-6 col-lg-auto ps-lg-0 pe-1 pe-sm-2">
40-
<Button size={above['lg'](deviceSize) || deviceSize === "xs" ? "sm" : ""} tag={Link} to="/login" color="primary" outline block
41-
>
42-
Log in
43-
</Button>
44-
</Col>
45-
<Col className="col-6 col-lg-auto ps-lg-0 ps-1 ps-sm-2">
46-
<Button size={above['lg'](deviceSize) || deviceSize === "xs" ? "sm" : ""} tag={Link} to="/register" color="secondary" block>
47-
Sign up
48-
</Button>
49-
</Col>
50-
</Row>}
51-
<div className={`h-100 ps-lg-4 content-video-container w-100 ${user?.loggedIn ? "pt-1 pt-sm-2 pt-lg-2" : "pt-4 pt-lg-3"} ${userConsent.cookieConsent?.youtubeCookieAccepted ?? false ? "ratio-16x9" : ""}`}>
52-
<HomepageYoutubeCookieHandler />
53-
</div>
54-
</Col>
55-
</Row>
56-
57-
<div className="physics-site-intro mt-4 mt-lg-2">
58-
<strong>Show me resources for...</strong>
59-
<Row className="mt-2">
60-
<Col xs={12} lg={3} className="pe-lg-1 py-1">
61-
<Button size={deviceSize==="xs" ? "sm" : ""} block tag={Link} to="/11_14" className="h-100 d-inline-flex align-items-center justify-content-center">
62-
11-14
63-
</Button>
64-
</Col>
65-
<Col xs={12} lg={3} className="px-lg-1 py-1">
66-
<Button size={deviceSize==="xs" ? "sm" : ""} block tag={Link} to="/gcse" className="h-100 d-inline-flex align-items-center justify-content-center">
67-
{above["md"](deviceSize) ?
68-
"GCSE or\u00A0equivalent" :
69-
"GCSE"}
70-
</Button>
71-
</Col>
72-
<Col xs={12} lg={3} className="px-lg-1 py-1">
73-
<Button size={deviceSize==="xs" ? "sm" : ""} block tag={Link} to="/alevel" className="h-100 d-inline-flex align-items-center justify-content-center">
74-
{above["md"](deviceSize) ?
75-
"A\u00A0Level or\u00A0equivalent" :
76-
"A\u00A0Level"}
77-
</Button>
78-
</Col>
79-
<Col xs={12} lg={3} className="ps-lg-1 py-1">
80-
<Button size={deviceSize==="xs" ? "sm" : ""} block tag={Link} to="/teacher_features" className="h-100 d-inline-flex align-items-center justify-content-center">
81-
teachers
82-
</Button>
83-
</Col>
84-
</Row>
85-
</div>
86-
</Container>
120+
<div id="homepage" className="homepage pb-5">
121+
<section id="student-dashboard">
122+
<StudentDashboard />
87123
</section>
88-
89-
<section id="news">
90-
<Container>
91-
<div className="h-underline mb-4 mt-4 pt-2 mt-sm-5 pt-sm-0 d-flex align-items-center">
92-
<h2>News and features</h2>
93-
<Link to="/news" className="ms-auto">See all news</Link>
94-
</div>
95-
<Row className="eventList pt-1">
96-
<Col>
97-
<NewsCarousel items={news} showTitle className={"mx-sm-n4"} />
98-
</Col>
99-
</Row>
100-
</Container>
124+
<section id="homepage-hero">
125+
{!isLoggedIn(user) && <HomepageHero />}
101126
</section>
102-
103-
{!user?.loggedIn && <section className="mb-4">
104-
<Container>
105-
<div className="mt-4 py-4 px-5 d-flex align-items-center flex-column flex-md-row border bg-white">
106-
<h3 className="text-center text-md-start me-md-4 me-lg-0 mb-3 mb-md-0">
107-
Sign up to track your progress
108-
</h3>
109-
<Button tag={Link} size="lg" className="ms-md-auto me-md-3 me-lg-5 btn-xl" to={"/register"}>
110-
Sign up
111-
</Button>
112-
</div>
113-
</Container>
114-
</section>}
127+
<Container>
128+
<section id="explore-learn">
129+
<div className="mt-5">
130+
<div className="d-flex">
131+
<h3>Explore and learn!</h3>
132+
<div className="section-divider ms-2"/>
133+
</div>
134+
<ListViewCards cards={cards}/>
135+
</div>
136+
</section>
137+
<section id="events-news">
138+
<Row className="mt-5 row-cols-1 row-cols-lg-2">
139+
<div className="d-flex flex-column mt-3">
140+
<div className="d-flex">
141+
<h3>Upcoming Events</h3>
142+
<Link to="/events" className="news-events-link">More events</Link>
143+
<div className="section-divider"/>
144+
</div>
145+
<ShowLoadingQuery
146+
query={eventsQuery}
147+
defaultErrorTitle={"Error loading events list"}
148+
thenRender={({events}) => {
149+
return <Row className="h-100">
150+
{events.map(event => <Col key={event.id}>
151+
<EventCard event={event} />
152+
</Col>)}
153+
</Row>;
154+
}}/>
155+
</div>
156+
<div className="d-flex flex-column mt-3">
157+
<div className="d-flex">
158+
<h3>News & Features</h3>
159+
<Link to="/news" className="news-events-link">More news</Link>
160+
<div className="section-divider"/>
161+
</div>
162+
{news && <Row className="h-100">
163+
{news.slice(0, 2).map(newsItem => <Col key={newsItem.id}>
164+
<NewsCard newsItem={newsItem} />
165+
</Col>)}
166+
</Row>}
167+
</div>
168+
</Row>
169+
</section>
170+
</Container>
115171
</div>
116172
</>;
117173
};

src/scss/phy/cards.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@container pod-parent (min-width: 736px) {
1+
@container pod-parent (min-width: 700px) {
22
.pod.card {
33
display: flex;
44
flex-direction: row;
@@ -16,6 +16,10 @@
1616
.card-body {
1717
padding-top: 0;
1818
}
19+
20+
.pod-title {
21+
font-size: 24px;
22+
}
1923
}
2024
}
2125

0 commit comments

Comments
 (0)