Skip to content

Commit 2e49cfb

Browse files
alecliu1204rokika1l-duong05Xx358hffy
authored
Revised UI draft (#376)
* added skeleton for new landingpage and course library pages * created components for landing page * completed new UI sponsors component (#371) * finished base structure of sponsors for dark mode * finished darkmode component * added dark and light mode logos * fixed border colouring for light mode * finalised styling of spacing and font bolding * finished new header component for landing page * header, sponsors and course pages ready * Began creating Features Page. Created divs and added Roboto font * Created Features Page and left svg files in Assets * done with draft of new ui --------- Co-authored-by: Rokika Kh <rokika.kh1@gmail.com> Co-authored-by: l-duong05 <176846912+l-duong05@users.noreply.github.com> Co-authored-by: l-duong05 <lduong05.git@gmail.com> Co-authored-by: William Lu <williamlu2016@gmail.com>
1 parent 7a6e7d2 commit 2e49cfb

File tree

19 files changed

+630
-25
lines changed

19 files changed

+630
-25
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Image from "next/image";
2+
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
3+
import navbar from "@/assets/navbar.svg";
4+
import Sponsorships from "@/components/SponsorshipsSection/Sponsorships";
5+
import { Metadata } from "next";
6+
import { ItemList, WithContext } from "schema-dts";
7+
import { get } from "@/utils/request";
8+
import { Course, Courses } from "@/types/api";
9+
10+
export default async function CourseLibraryPage() {
11+
const { courses: initialCourses } = (await get(
12+
"/courses?offset=0",
13+
)) as Courses;
14+
15+
return (
16+
<div>
17+
{/* TOP OF PAGE */}
18+
{/* Title here */}
19+
<h1 className="font-bold text-unilectives-blue text-[50px] xs:ml-8 sm:ml-8 md:ml-8 ml-20 pt-12">
20+
Search courses
21+
</h1>
22+
{/* Course cards */}
23+
<div className="flex flex-col justify-center items-center mt-10">
24+
<LandingPageContent initialCourses={initialCourses} />
25+
</div>
26+
{/* BOTTOM OF PAGE */}
27+
</div>
28+
);
29+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"use client";
2+
import Image, { StaticImageData } from "next/image";
3+
import { useState, useEffect } from "react";
4+
import feature_box_one from "../../assets/features/feature_box_one.svg";
5+
import feature_box_two from "../../assets/features/feature_box_two.svg";
6+
import feature_box_three from "../../assets/features/feature_box_three.svg";
7+
import Star from "@/assets/star.png";
8+
import Account from "@/assets/account.png";
9+
import Comment from "@/assets/comment.png";
10+
import { ReactNode } from "react";
11+
12+
interface FeatureBoxProps {
13+
title: string;
14+
description: string;
15+
image: StaticImageData; // new prop for the image
16+
}
17+
18+
const FeatureBox: React.FC<FeatureBoxProps> = ({
19+
title,
20+
description,
21+
image,
22+
}) => {
23+
const [windowWidth, setWindowWidth] = useState<number>(0);
24+
25+
useEffect(() => {
26+
setWindowWidth(window.innerWidth);
27+
const handleResize = () => setWindowWidth(window.innerWidth);
28+
window.addEventListener("resize", handleResize);
29+
return () => window.removeEventListener("resize", handleResize);
30+
}, []);
31+
32+
const showImage = windowWidth > 645;
33+
34+
return (
35+
<div
36+
className="
37+
flex flex-col p-6 bg-[#EDEEEF] dark:bg-[#263245]
38+
shadow-md hover:shadow-lg transition-shadow duration-300
39+
border-l-2 border-blue-400 dark:border-blue-600
40+
w-[300px] h-[250px]
41+
sm:w-[260px] sm:h-[200px]
42+
md:w-[280px] md:h-[220px]
43+
"
44+
>
45+
{showImage && (
46+
<div className="mb-4">
47+
<Image src={image} alt={title} className="w-12 h-12 object-contain" />
48+
</div>
49+
)}
50+
51+
{/* Text */}
52+
<h3
53+
className="
54+
font-extrabold text-unilectives-blue dark:text-white mb-2
55+
"
56+
>
57+
{title}
58+
</h3>
59+
60+
<p
61+
className="
62+
text-gray-700 dark:text-gray-300
63+
"
64+
>
65+
{description}
66+
</p>
67+
</div>
68+
);
69+
};
70+
71+
export default function Features() {
72+
return (
73+
<div className="flex flex-col items-center mt-0 mb-0 w-full mb-10">
74+
<h2 className="text-3xl font-bold text-center mb-8 mt-5">Our Features</h2>
75+
76+
<div className="flex flex-row md:flex-col justify-center items-center gap-8 w-full px-4">
77+
<FeatureBox
78+
title="Comprehensive Course Reviews"
79+
description="Real student insights on difficulty, enjoyment, and teaching quality—all in one place."
80+
image={Star}
81+
/>
82+
<FeatureBox
83+
title="Review Courses"
84+
description="Share your experience to help fellow students make smarter study choices."
85+
image={Comment}
86+
/>
87+
<FeatureBox
88+
title="Your Account"
89+
description="Save reviews and personalise your experience."
90+
image={Account}
91+
/>
92+
</div>
93+
</div>
94+
);
95+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import UnilectivesLogo from "@/assets/unilectives-logo.png";
2+
import Image from "next/image";
3+
4+
export default function Header() {
5+
return (
6+
<div className="flex flex-row w-full justify-center items-center pb-3 bg-gradient-to-b lg:px-10">
7+
<div
8+
className="flex flex-row max-w-5xl justify-center items-center gap-20 md:gap-10 lg:gap-15
9+
px-4 sm:px-2"
10+
>
11+
{/* Left side text */}
12+
<div className="flex flex-col w-full gap-3">
13+
<p className="drop-shadow-md text-base sm:text-sm xs:text-xs">
14+
DevSoc presents
15+
</p>
16+
17+
{/* Smaller size on very small screens */}
18+
<h1
19+
className="font-bold text-unilectives-blue
20+
text-8xl sm:text-4xl md:text-6xl lg:text-7xl
21+
xs:text-3xl leading-none"
22+
>
23+
unilectives
24+
</h1>
25+
26+
<p className="font-[450] text-3xl sm:text-md md:text-xl lg:text-2xl xs:text-base">
27+
Your one-stop shop for UNSW course reviews.
28+
</p>
29+
30+
<a href="/NEW-course-library-page">
31+
<button
32+
className="bounce-every-10s inline-flex w-fit transition-all duration-500
33+
hover:-translate-y-1 items-center mt-6
34+
gap-1 px-8 py-2 xs:px-5 xs:py-1.5
35+
bg-unilectives-icon dark:bg-unilectives-icon/85
36+
dark:hover:bg-unilectives-icon/80 text-white rounded-md
37+
hover:bg-unilectives-icon/95"
38+
>
39+
Explore courses
40+
</button>
41+
</a>
42+
</div>
43+
44+
{/* Hide the logo on small screens */}
45+
<Image
46+
className="w-80 sm:w-32 md:w-48 lg:w-64 xs:hidden"
47+
src={UnilectivesLogo}
48+
alt="Unilectives Logo"
49+
/>
50+
</div>
51+
</div>
52+
);
53+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Sponsors() {
2+
return (
3+
<div>
4+
<p>Sponsors...</p>
5+
</div>
6+
);
7+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import Image from "next/image";
2+
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
3+
import navbar from "@/assets/navbar.svg";
4+
import OldSponsorships from "@/components/SponsorshipsSection/Sponsorships";
5+
import { Metadata } from "next";
6+
import { ItemList, WithContext } from "schema-dts";
7+
import { get } from "@/utils/request";
8+
import { Course, Courses } from "@/types/api";
9+
import NewSponsorships from "@/components/SponsorshipsSection/NewSponsorships";
10+
import Header from "./Header";
11+
import Features from "./Features";
12+
13+
// Metadata to assist SEO - provies metadata for HTML head section
14+
export async function generateMetadata(): Promise<Metadata> {
15+
return {
16+
title: `Home | Unilectives - UNSW Course Reviews`,
17+
description: `UNSW course reviews, ratings, and study tips. Unilectives is your one-stop shop for making informed course choices at UNSW.`,
18+
};
19+
}
20+
21+
export default async function LandingPage() {
22+
// GET request for all courses
23+
const { courses: initialCourses } = (await get(
24+
"/courses?offset=0",
25+
)) as Courses;
26+
27+
// Generate metadata to help with SEO (inject via script in return)
28+
const metaLD: WithContext<ItemList> = {
29+
"@context": "https://schema.org",
30+
"@type": "ItemList",
31+
itemListElement: initialCourses.map((course: Course, index: number) => ({
32+
"@type": "ListItem",
33+
position: index + 1,
34+
item: {
35+
"@type": "Course",
36+
url: `//www.handbook.unsw.edu.au/undergraduate/courses/${new Date().getFullYear()}/${
37+
course.courseCode
38+
}`,
39+
name: course.title,
40+
description: course.description,
41+
provider: {
42+
"@type": "CollegeOrUniversity",
43+
name: "University of New South Wales",
44+
sameAs: "https://www.unsw.edu.au/",
45+
},
46+
aggregateRating: {
47+
"@type": "AggregateRating",
48+
ratingCount: course.reviewCount,
49+
ratingValue: course.reviewCount === 0 ? 0 : course.overallRating,
50+
bestRating: 5,
51+
},
52+
offers: [
53+
{
54+
"@type": "Offer",
55+
category: "Paid",
56+
},
57+
],
58+
hasCourseInstance: course.terms.map((term: number) => ({
59+
"@type": "CourseInstance",
60+
courseMode: "Blended",
61+
courseSchedule: {
62+
"@type": "Schedule",
63+
repeatCount: term === 0 ? 5 : 10,
64+
repeatFrequency: "Weekly",
65+
},
66+
})),
67+
},
68+
})),
69+
};
70+
71+
return (
72+
<div>
73+
{/* SCRIPT FOR SEO - do not touch*/}
74+
<script
75+
type="application/ld+json"
76+
dangerouslySetInnerHTML={{ __html: JSON.stringify(metaLD) }}
77+
/>
78+
{/* Landing page graphic */}
79+
<Image
80+
src={navbar}
81+
width={1000}
82+
height={500}
83+
alt="landing page graphic"
84+
layout="responsive"
85+
priority
86+
className="block"
87+
/>
88+
{/* SECTION 1 - HEADER */}
89+
<Header />
90+
{/* SECTION 2 - "OUR FEATURES" */}
91+
<Features />
92+
{/* SECTION 3 - "PROUDLY SPONSORED BY" */}
93+
<NewSponsorships />
94+
{/* BOTTOM OF PAGE */}
95+
</div>
96+
);
97+
}

frontend/src/app/globals.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,20 @@
2727
}
2828
}
2929
}
30+
31+
@keyframes gentleBounce {
32+
0%,
33+
85%,
34+
100% {
35+
transform: translateY(0);
36+
}
37+
90% {
38+
transform: translateY(-6px);
39+
}
40+
95% {
41+
transform: translateY(-3px);
42+
}
43+
}
44+
.bounce-every-10s {
45+
animation: gentleBounce 10s ease-in-out infinite;
46+
}

0 commit comments

Comments
 (0)