Skip to content

Commit

Permalink
dev to main (#252)
Browse files Browse the repository at this point in the history
* frontend(feature)/elec 510: terms and conditions (#240)

* feature/ELEC-510: initial commit

* feature/ELEC-510: styling

* feature/ELEC-510: fixed margin issue

* FIX

* fix

* fix link

* fix escapes

* logout button color

---------

Co-authored-by: Aimen Hamed <[email protected]>

* fix link

* add ts and cs link

* chore(deps): update actions/checkout action to v4 (#243)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency eslint-config-prettier to v9 (#235)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: fixed bug with course first reviews, course with only no text reviews, and optimistic ui updates changes back when changing tabs on profile page (#242)

* fix auth session length

* feat(frontend): add rich results to home and course pages (#250)

* wip(rich-results): add course-specific LD

* wip(rich-results): account for 0 case

* wip(rich-results): add course URL

* feat(frontend): add metadata descriptions

* docs(backend): correct typo in prisma migration instructions

* chore(frontend): demote excess h1 tags

* feat(frontend): add course list LD on landing page

* refactor: clean up rating count and value logic

* chore: resolve comments

---------

Co-authored-by: Jared L <[email protected]>

* fix(frontend)/ELEC-520: Added SSR on the first 25 courses  (#251)

* frontend(feature)/elec 510: terms and conditions (#240) (#241)

* frontend(feature)/elec 510: terms and conditions (#240)

* feature/ELEC-510: initial commit

* feature/ELEC-510: styling

* feature/ELEC-510: fixed margin issue

* FIX

* fix

* fix link

* fix escapes

* logout button color

---------

Co-authored-by: Aimen Hamed <[email protected]>

* fix link

* add ts and cs link

---------

Co-authored-by: Dylan W <[email protected]>

* fix: fixed bug with course first reviews, course with only no text reviews, and optimistic ui updates changes back when changing tabs on profile page

* chore: added ssr rendering on the first 25 courses

---------

Co-authored-by: Aimen <[email protected]>
Co-authored-by: Dylan W <[email protected]>

* fix: course code returning undefined

* fix(frontend): course page hydration

---------

Co-authored-by: Dylan W <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Merry Rosalie <[email protected]>
Co-authored-by: joanna209 <[email protected]>
Co-authored-by: Jared L <[email protected]>
  • Loading branch information
6 people authored Sep 27, 2023
1 parent 2cbda4b commit f57bd7c
Show file tree
Hide file tree
Showing 25 changed files with 206 additions and 88 deletions.
2 changes: 1 addition & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ First start the database:
```
docker run --rm -itd --network cselectives-network --name cselectives-db -e POSTGRES_PASSWORD=password -e POSTGRES_DB=mydb -p 5432:5432 postgres
# Initialise db schema:
DATABASE_URL="postgresql://postgres:password@0.0.0.0:5432/mydb?schema=cselectives" npx prisma migrate dev
DATABASE_URL="postgresql://postgres:password@0.0.0.0:5432/mydb?schema=unilectives" npx prisma migrate dev
```
Then dragonfly (redis):
Expand Down
5 changes: 3 additions & 2 deletions frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
"typescript.enablePromptUseWorkspaceTsdk": true,
"githubPullRequests.ignoredPullRequestBranches": ["develop"]
}
9 changes: 9 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"next-auth": "^4.22.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"schema-dts": "^1.1.2",
"tailwind-scrollbar": "^3.0.1",
"typescript": "5.0.3"
},
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import AdminContent from "@/components/AdminContent/AdminContent";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import navbar from "@/assets/navbar.svg";
import { Metadata } from "next";

export default async function () {
export const metadata: Metadata = {
title: `Admin Dashboard | Unilectives - UNSW Course Reviews`,
description: `Manage and resolve reports left on particular course reviews by Unilectives users.`,
}

export default async function AdminDashboard() {
const session = await getServerSession(authOptions);
const { reviews } = (await validatedReq(
"GET",
Expand All @@ -32,7 +38,7 @@ export default async function () {
priority
/>
<div className="flex flex-col w-full px-12 py-6 gap-6">
<p className="font-bold text-3xl">Dashboard</p>
<p className="font-bold text-3xl">Admin Dashboard</p>
<p>Welcome to the admin dashboard!</p>
<AdminContent
reports={reports}
Expand Down
60 changes: 51 additions & 9 deletions frontend/src/app/course/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import Image from "next/image";
import waves from "../../../assets/waves.svg";
import { LinkIcon } from "@heroicons/react/24/solid";
import { Suspense } from "react";
import TermsGroup from "@/components/TermsGroup/TermsGroup";
import Link from "next/link";
import DoughnutChart from "@/components/DoughnutChart/DoughnutChart";
import { notFound } from "next/navigation";
import ReviewsBar from "@/components/ReviewsBar/ReviewsBar";
import Rating from "@/components/Rating/Rating";
import ReviewSearchbar from "@/components/ReviewSearchBar/ReviewSearchBar";
import ReviewsBar from "@/components/ReviewsBar/ReviewsBar";
import TermsGroup from "@/components/TermsGroup/TermsGroup";
import { authOptions } from "@/lib/auth";
import { Course, Reviews } from "@/types/api";
import { get, validatedReq } from "@/utils/request";
import { LinkIcon } from "@heroicons/react/24/solid";
import { Metadata } from "next";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";
import { Suspense } from "react";
import { AggregateRating, WithContext } from "schema-dts";
import waves from "../../../assets/waves.svg";
import { signOut } from "next-auth/react";

export async function generateMetadata(props: {
params: {
[key: string]: string;
};
}): Promise<Metadata> {
const { course } = (await get(`/course/${props.params.id.toUpperCase()}`)) as {
course: Course;
};
return {
title: `${course.courseCode} | Unilectives - UNSW Course Reviews`,
description: `Considering ${course.courseCode} at UNSW? Dive into real student reviews giving you unfiltered perspectives on the course. Get the scoop on teaching quality, workload, and more.`,
};
}

export default async function ReviewPage({
params,
}: {
Expand Down Expand Up @@ -48,6 +64,28 @@ export default async function ReviewPage({
}
}

const metaLD: WithContext<AggregateRating> = {
"@context": "https://schema.org",
"@type": "AggregateRating",
ratingCount: course.reviewCount,
ratingValue: course.reviewCount === 0 ? 0 : course.overallRating,
bestRating: 5,
itemReviewed: {
"@type": "Course",
name: course.title,
courseCode: course.courseCode,
description: course.description,
url: `//www.handbook.unsw.edu.au/undergraduate/courses/${new Date().getFullYear()}/${
course.courseCode
}`,
provider: {
"@type": "CollegeOrUniversity",
name: "University of New South Wales",
sameAs: "https://www.unsw.edu.au/",
},
},
};

return (
<div className="isolate">
{/* Header */}
Expand All @@ -66,6 +104,10 @@ export default async function ReviewPage({
{/* Course details */}
<div className="flex gap-8 pt-12 px-16 md:px-8 lg:pt-8 md:flex-wrap">
<Suspense fallback={<div>Loading...</div>}>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(metaLD) }}
/>
<section className="space-y-4 w-full block md:static md:max-h-full sticky top-8 max-h-[calc(100vh-4rem)] overflow-y-scroll scrollbar-none">
<h1 className="text-6xl font-bold break-words">
{course.courseCode}
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import { authOptions } from "@/lib/auth";
import { AlertProvider } from "@/lib/snackbar-context";
import Navbar from "@/components/Navbar/Navbar";

export const metadata = {
title: "uni-lectives",
description: "Course review website for UNSW made by CSESoc",
};

export default async function RootLayout({
children,
}: {
Expand Down
77 changes: 61 additions & 16 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,76 @@
import Image from "next/image";
import LandingPageContent from "@/components/LandingPageContent/LandingPageContent";
import navbar from "@/assets/navbar.svg";
import { Metadata } from "next";
import { ItemList, WithContext } from "schema-dts";
import { get } from "@/utils/request";
import { Course, Courses } from "@/types/api";

export async function generateMetadata(): Promise<Metadata> {
return {
title: `Home | Unilectives - UNSW Course Reviews`,
description: `A course review website for UNSW made by CSESoc`,
};
}

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

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,
},
},
})),
};

export default function Home() {
return (
<div className="mb-20">
{/* Navbar */}
<div>
<Image
src={navbar}
width={1000}
height={500}
alt="landing page graphic"
layout="responsive"
priority
/>
</div>
{/* Landing page graphic */}
<Image
src={navbar}
width={1000}
height={500}
alt="landing page graphic"
layout="responsive"
priority
/>
{/* Hero Section */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(metaLD) }}
/>
<div className="flex flex-row w-full justify-center items-center mt-10">
<div className="flex flex-row w-5/6 space-y-0 justify-between items-left md:space-y-4 md:flex-col md:items-center">
<div className="flex flex-col w-full gap-3">
<p className="drop-shadow-md text-base sm:text-xs">
CSESoc presents
</p>
<p className="justify-center font-bold text-unilectives-blue text-7xl sm:text-4xl">
uni-lectives
</p>
<h1 className="justify-center font-bold text-unilectives-blue text-7xl sm:text-4xl">
unilectives
</h1>
<p className="justify-center font-semibold text-base sm:text-xs">
Your one-stop shop for UNSW course and elective reviews.
</p>
Expand All @@ -34,7 +79,7 @@ export default function Home() {
</div>
{/* Course Section */}
<div className="flex flex-col justify-center items-center mt-10">
<LandingPageContent />
<LandingPageContent initialCourses={initialCourses} />
</div>
</div>
);
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/app/terms-and-conditions/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export default function TermsAndConditionsPage() {
import { Metadata } from "next";

export const metadata: Metadata = {
title: `Terms & Conditions | Unilectives - UNSW Course Reviews`,
description: `Terms and Conditions for Unilectives - UNSW Course Reviews`,
}

export default function TermsAndConditions() {
return (
<div className="p-20 xs:m-12 sm:m-12 xs:text-xs sm:text-sm">
<h1 className="text-lg font-bold">Terms and Conditions</h1>
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/app/user/[zid]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import { validatedReq } from "@/utils/request";
import UserPageContent from "@/components/UserPageContent/UserPageContent";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { Metadata } from "next";

export async function generateMetadata(props: {
params: {
[key: string]: string;
};
}): Promise<Metadata> {
return {
title: `${props.params.zid} Profile | Unilectives - UNSW Course Reviews`,
description: `Curious about ${props.params.zid}'s reviews? Check out their profile to see what courses they've reviewed and bookmarked.`,
};
}

export default async function UserPage({
params,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/CourseCard/CourseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function CourseCard({
<div className="box-border px-6 py-7 bg-unilectives-card hover:bg-gray-100 shadow-lg rounded-xl space-y-2 cursor-pointer">
{/* Course courseCode + Ratings */}
<div className="flex flex-wrap justify-between text-2xl gap-x-4">
<h1 className="font-bold w-[8ch]">{courseCode}</h1>
<h2 className="font-bold w-[8ch]">{courseCode}</h2>
<div className="text-left">
{/* StarRating */}
<div className="text-2xl inline">
Expand Down
29 changes: 12 additions & 17 deletions frontend/src/components/CoursesList/CoursesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import { get } from "@/utils/request";
import { sortCourses } from "@/utils/sortCourses";
import SortDropdown from "../SortDropdown/SortDropdown";

export default function CoursesList({ searchTerm }: { searchTerm?: string }) {
export default function CoursesList({
initialCourses,
searchTerm,
}: {
initialCourses: Course[];
searchTerm: string;
}) {
const courseFinishedRef = useRef(false);
const indexRef = useRef(0);
const indexRef = useRef(initialCourses.length);
const searchCoursesRef = useRef<Course[]>([]);

const [displayCourses, setDisplayCourses] = useState<Course[]>([]);
const [displayCourses, setDisplayCourses] =
useState<Course[]>(initialCourses);
const [initialLoading, setInitialLoading] = useState(true);
const [selected, setSelected] = useState("");

Expand Down Expand Up @@ -58,7 +65,7 @@ export default function CoursesList({ searchTerm }: { searchTerm?: string }) {
useEffect(() => {
const resetRefs = () => {
courseFinishedRef.current = false;
indexRef.current = 0;
indexRef.current = initialCourses.length;
searchCoursesRef.current = [];
};
const getSearchResults = async () => {
Expand All @@ -74,20 +81,8 @@ export default function CoursesList({ searchTerm }: { searchTerm?: string }) {
indexRef.current += paginationOffset;
setInitialLoading(false);
};
const getDefaultResults = async () => {
try {
const { courses } = (await get(`/courses?offset=0`)) as Courses;
setDisplayCourses(courses);
indexRef.current += paginationOffset;
} catch (err) {
setDisplayCourses([]);
}
setInitialLoading(false);
};
const getInitialDisplayCourses = () => {
if (searchTerm === "") {
getDefaultResults();
} else {
if (searchTerm !== "") {
getSearchResults();
}
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DeleteModal/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default function DeleteModal({
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w full max-w-md transform overflow-hidden rounded-md text-left align-middle shadow-xl transition-all bg-unilectives-modal px-8 py-6 space-y-4 isolate">
<Dialog.Title as="h1" className="text-2xl font-bold">
<Dialog.Title as="h2" className="text-2xl font-bold">
Delete {group.charAt(0).toUpperCase() + group.slice(1)}
</Dialog.Title>
<hr className="border-black/25"></hr>
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/EditReviewModal/EditReviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function EditReviewModal({
<Dialog.Panel className="w-full max-w-5xl transform overflow-hidden rounded-md text-left align-middle shadow-xl transition-all bg-unilectives-modal px-12 py-8 space-y-5 isolate">
{/* Modal title + close button */}
<div className="flex justify-between items-center">
<Dialog.Title as="h1" className="text-2xl font-bold">
<Dialog.Title as="h2" className="text-2xl font-bold">
Edit Your Review
</Dialog.Title>
<button onClick={closeModal}>
Expand Down Expand Up @@ -186,10 +186,9 @@ export default function EditReviewModal({
/>
</div>
<div className="flex flex-wrap gap-5 justify-between items-center">
{/* Terms & Condition */}
{/* Terms & Conditions */}
<p>
By clicking Confirm, you have agreed to the{" "}
{/* TODO: Change link to the Terms and Condition page */}
<Link
href="/"
target="_blank"
Expand Down
Loading

0 comments on commit f57bd7c

Please sign in to comment.