Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions frontend/src/app/NEW-course-library-page/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Image from "next/image";
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
import navbar from "@/assets/navbar.svg";
import Sponsorships from "@/components/SponsorshipsSection/Sponsorships";
import { Metadata } from "next";
import { ItemList, WithContext } from "schema-dts";
import { get } from "@/utils/request";
import { Course, Courses } from "@/types/api";

export default async function CourseLibraryPage() {
const { courses: initialCourses } = (await get(
"/courses?offset=0",
)) as Courses;

return (
<div>
{/* TOP OF PAGE */}
{/* Title here */}
<h1 className="font-bold text-unilectives-blue text-[50px] xs:ml-8 sm:ml-8 md:ml-8 ml-20 pt-12">
Search courses
</h1>
{/* Course cards */}
<div className="flex flex-col justify-center items-center mt-10">
<LandingPageContent initialCourses={initialCourses} />
</div>
{/* BOTTOM OF PAGE */}
</div>
);
}
95 changes: 95 additions & 0 deletions frontend/src/app/NEW-landing-page/Features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client";
import Image, { StaticImageData } from "next/image";
import { useState, useEffect } from "react";
import feature_box_one from "../../assets/features/feature_box_one.svg";
import feature_box_two from "../../assets/features/feature_box_two.svg";
import feature_box_three from "../../assets/features/feature_box_three.svg";
import Star from "@/assets/star.png";
import Account from "@/assets/account.png";
import Comment from "@/assets/comment.png";
import { ReactNode } from "react";

interface FeatureBoxProps {
title: string;
description: string;
image: StaticImageData; // new prop for the image
}

const FeatureBox: React.FC<FeatureBoxProps> = ({
title,
description,
image,
}) => {
const [windowWidth, setWindowWidth] = useState<number>(0);

useEffect(() => {
setWindowWidth(window.innerWidth);
const handleResize = () => setWindowWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);

const showImage = windowWidth > 645;

return (
<div
className="
flex flex-col p-6 bg-[#EDEEEF] dark:bg-[#263245]
shadow-md hover:shadow-lg transition-shadow duration-300
border-l-2 border-blue-400 dark:border-blue-600
w-[300px] h-[250px]
sm:w-[260px] sm:h-[200px]
md:w-[280px] md:h-[220px]
"
>
{showImage && (
<div className="mb-4">
<Image src={image} alt={title} className="w-12 h-12 object-contain" />
</div>
)}

{/* Text */}
<h3
className="
font-extrabold text-unilectives-blue dark:text-white mb-2
"
>
{title}
</h3>

<p
className="
text-gray-700 dark:text-gray-300
"
>
{description}
</p>
</div>
);
};

export default function Features() {
return (
<div className="flex flex-col items-center mt-0 mb-0 w-full mb-10">
<h2 className="text-3xl font-bold text-center mb-8 mt-5">Our Features</h2>

<div className="flex flex-row md:flex-col justify-center items-center gap-8 w-full px-4">
<FeatureBox
title="Comprehensive Course Reviews"
description="Real student insights on difficulty, enjoyment, and teaching quality—all in one place."
image={Star}
/>
<FeatureBox
title="Review Courses"
description="Share your experience to help fellow students make smarter study choices."
image={Comment}
/>
<FeatureBox
title="Your Account"
description="Save reviews and personalise your experience."
image={Account}
/>
</div>
</div>
);
}
53 changes: 53 additions & 0 deletions frontend/src/app/NEW-landing-page/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import UnilectivesLogo from "@/assets/unilectives-logo.png";
import Image from "next/image";

export default function Header() {
return (
<div className="flex flex-row w-full justify-center items-center pb-3 bg-gradient-to-b lg:px-10">
<div
className="flex flex-row max-w-5xl justify-center items-center gap-20 md:gap-10 lg:gap-15
px-4 sm:px-2"
>
{/* Left side text */}
<div className="flex flex-col w-full gap-3">
<p className="drop-shadow-md text-base sm:text-sm xs:text-xs">
DevSoc presents
</p>

{/* Smaller size on very small screens */}
<h1
className="font-bold text-unilectives-blue
text-8xl sm:text-4xl md:text-6xl lg:text-7xl
xs:text-3xl leading-none"
>
unilectives
</h1>

<p className="font-[450] text-3xl sm:text-md md:text-xl lg:text-2xl xs:text-base">
Your one-stop shop for UNSW course reviews.
</p>

<a href="/NEW-course-library-page">
<button
className="bounce-every-10s inline-flex w-fit transition-all duration-500
hover:-translate-y-1 items-center mt-6
gap-1 px-8 py-2 xs:px-5 xs:py-1.5
bg-unilectives-icon dark:bg-unilectives-icon/85
dark:hover:bg-unilectives-icon/80 text-white rounded-md
hover:bg-unilectives-icon/95"
>
Explore courses
</button>
</a>
</div>

{/* Hide the logo on small screens */}
<Image
className="w-80 sm:w-32 md:w-48 lg:w-64 xs:hidden"
src={UnilectivesLogo}
alt="Unilectives Logo"
/>
</div>
</div>
);
}
7 changes: 7 additions & 0 deletions frontend/src/app/NEW-landing-page/Sponsors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Sponsors() {
return (
<div>
<p>Sponsors...</p>
</div>
);
}
97 changes: 97 additions & 0 deletions frontend/src/app/NEW-landing-page/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Image from "next/image";
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
import navbar from "@/assets/navbar.svg";
import OldSponsorships from "@/components/SponsorshipsSection/Sponsorships";
import { Metadata } from "next";
import { ItemList, WithContext } from "schema-dts";
import { get } from "@/utils/request";
import { Course, Courses } from "@/types/api";
import NewSponsorships from "@/components/SponsorshipsSection/NewSponsorships";
import Header from "./Header";
import Features from "./Features";

// Metadata to assist SEO - provies metadata for HTML head section
export async function generateMetadata(): Promise<Metadata> {
return {
title: `Home | Unilectives - UNSW Course Reviews`,
description: `UNSW course reviews, ratings, and study tips. Unilectives is your one-stop shop for making informed course choices at UNSW.`,
};
}

export default async function LandingPage() {
// GET request for all courses
const { courses: initialCourses } = (await get(
"/courses?offset=0",
)) as Courses;

// Generate metadata to help with SEO (inject via script in return)
const metaLD: WithContext<ItemList> = {
"@context": "https://schema.org",
"@type": "ItemList",
itemListElement: initialCourses.map((course: Course, index: number) => ({
"@type": "ListItem",
position: index + 1,
item: {
"@type": "Course",
url: `//www.handbook.unsw.edu.au/undergraduate/courses/${new Date().getFullYear()}/${
course.courseCode
}`,
name: course.title,
description: course.description,
provider: {
"@type": "CollegeOrUniversity",
name: "University of New South Wales",
sameAs: "https://www.unsw.edu.au/",
},
aggregateRating: {
"@type": "AggregateRating",
ratingCount: course.reviewCount,
ratingValue: course.reviewCount === 0 ? 0 : course.overallRating,
bestRating: 5,
},
offers: [
{
"@type": "Offer",
category: "Paid",
},
],
hasCourseInstance: course.terms.map((term: number) => ({
"@type": "CourseInstance",
courseMode: "Blended",
courseSchedule: {
"@type": "Schedule",
repeatCount: term === 0 ? 5 : 10,
repeatFrequency: "Weekly",
},
})),
},
})),
};

return (
<div>
{/* SCRIPT FOR SEO - do not touch*/}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(metaLD) }}
/>
{/* Landing page graphic */}
<Image
src={navbar}
width={1000}
height={500}
alt="landing page graphic"
layout="responsive"
priority
className="block"
/>
{/* SECTION 1 - HEADER */}
<Header />
{/* SECTION 2 - "OUR FEATURES" */}
<Features />
{/* SECTION 3 - "PROUDLY SPONSORED BY" */}
<NewSponsorships />
{/* BOTTOM OF PAGE */}
</div>
);
}
29 changes: 29 additions & 0 deletions frontend/src/app/course-library/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Image from "next/image";
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
import navbar from "@/assets/navbar.svg";
import Sponsorships from "@/components/SponsorshipsSection/Sponsorships";
import { Metadata } from "next";
import { ItemList, WithContext } from "schema-dts";
import { get } from "@/utils/request";
import { Course, Courses } from "@/types/api";

export default async function CourseLibraryPage() {
const { courses: initialCourses } = (await get(
"/courses?offset=0",
)) as Courses;

return (
<div>
{/* TOP OF PAGE */}
{/* Title here */}
<h1 className="font-bold text-unilectives-blue text-[50px] xs:ml-8 sm:ml-8 md:ml-8 ml-20 pt-12">
Search courses
</h1>
{/* Course cards */}
<div className="flex flex-col justify-center items-center mt-10">
<LandingPageContent initialCourses={initialCourses} />
</div>
{/* BOTTOM OF PAGE */}
</div>
);
}
17 changes: 17 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,20 @@
}
}
}

@keyframes gentleBounce {
0%,
85%,
100% {
transform: translateY(0);
}
90% {
transform: translateY(-6px);
}
95% {
transform: translateY(-3px);
}
}
.bounce-every-10s {
animation: gentleBounce 10s ease-in-out infinite;
}
Loading