Skip to content

Commit

Permalink
feat: refactor landing page, make example cards fetch current data (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinWu098 authored Apr 12, 2024
1 parent 70b721f commit 665ecc9
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 130 deletions.
128 changes: 6 additions & 122 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,12 @@
import { Button, buttonVariants } from "@/components/ui/button";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";

import { cn } from "@/lib/utils";
import { HelpCircle, Search } from "lucide-react";
import { Search } from "lucide-react";
import Link from "next/link";

const EXAMPLES = [
{
uni: "UC Irvine",
ge: "GE IV - Arts and Humanities",
courses: 75,
link: "/search?uni=University%20of%20California%2C%20Irvine&ge=GE%20IV",
},
{
uni: "UC Santa Barbara",
ge: "GE E - Culture and Thought",
courses: 75,
link: "/search?uni=University%20of%20California%2C%20Santa%20Barbara&ge=GE%20E",
},
{
uni: "UC Irvine",
ge: "GE VII - Multicultural Studies",
courses: 50,
link: "/search?uni=University%20of%20California%2C%20Irvine&ge=GE%20VII",
},
];

const ArticulableDefinition = () => {
return (
<Popover>
<PopoverTrigger aria-label="definition">
<HelpCircle className="inline-block h-4 w-4" />
</PopoverTrigger>
<PopoverContent>
<p className="text-sm text-muted-foreground">
&quot;An articulated course is a course... that can be used
to satisfy... general education requirements at another
college or university.&quot; - <br />
<Link
href={
"https://www.sdmesa.edu/about-mesa/administration/articulation/homepage-docs/Articulated%20vs%20Transferable.pdf"
}
referrerPolicy="no-referrer"
target="_blank"
>
<i>
<u>San Diego Mesa College</u>
</i>
</Link>
</p>
</PopoverContent>
</Popover>
);
};

const Graphics = () => {
return (
<div>
<div className="relative isolate">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 -top-40 -z-10 transform-gpu blur-[52px] sm:-top-48"
>
<div
style={{
clipPath:
"polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
}}
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[26.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-blue-500 to-purple-500 opacity-30 sm:left-[calc(50%-30rem)] sm:w-[42.1875rem]"
/>
</div>
</div>
<div className="relative isolate hidden md:flex">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 -top-40 -z-10 transform-gpu blur-[52px] sm:-top-72"
>
<div
style={{
clipPath:
"polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
}}
className="relative aspect-[1155/678] w-[16.125rem] rotate-[30deg] bg-gradient-to-tr from-blue-500 to-purple-500 opacity-30 sm:w-[22.1875rem] md:-right-[0vw] lg:-right-[12vw] xl:-right-[20vw]"
/>
</div>
</div>
</div>
);
};
import Examples from "@/components/hero/Examples";
import Graphics from "@/components/hero/Graphics";
import ArticulableDefinition from "@/components/hero/ArticulableDefinition";

export default function Home() {
return (
Expand Down Expand Up @@ -168,32 +77,7 @@ export default function Home() {
</div>
</div>

<div className="flex flex-wrap gap-4 px-6 sm:justify-center lg:px-8">
{EXAMPLES.map((example) => (
<Link
href={example.link}
key={example.uni + example.ge}
>
<Card className="w-[275px] drop-shadow-md hover:shadow-md sm:w-[300px]">
<CardHeader>
<CardTitle>{example.uni}</CardTitle>
<CardDescription>
{example.ge}
</CardDescription>
</CardHeader>
<CardFooter className="flex text-neutral-600">
<span className="font-bold text-primary">
<b>{example.courses}+</b>
</span>
&nbsp;Courses&nbsp;
<span className="hidden sm:flex">
Found
</span>
</CardFooter>
</Card>
</Link>
))}
</div>
<Examples />
</div>
</main>
);
Expand Down
38 changes: 38 additions & 0 deletions components/hero/ArticulableDefinition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { HelpCircle } from "lucide-react";

import Link from "next/link";

const ArticulableDefinition = () => {
return (
<Popover>
<PopoverTrigger aria-label="definition">
<HelpCircle className="inline-block h-4 w-4" />
</PopoverTrigger>
<PopoverContent>
<p className="text-sm text-muted-foreground">
&quot;An articulated course is a course... that can be used
to satisfy... general education requirements at another
college or university.&quot; - <br />
<Link
href={
"https://www.sdmesa.edu/about-mesa/administration/articulation/homepage-docs/Articulated%20vs%20Transferable.pdf"
}
referrerPolicy="no-referrer"
target="_blank"
>
<i>
<u>San Diego Mesa College</u>
</i>
</Link>
</p>
</PopoverContent>
</Popover>
);
};

export default ArticulableDefinition;
89 changes: 89 additions & 0 deletions components/hero/Examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use client";

import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { DatabaseReturn, queryDatabase } from "@/lib/utils/query-db";
import Link from "next/link";
import { useEffect, useState } from "react";

interface Example {
name: string;
geText: string;
institution: string;
ge: string;
link: string;
}

const EXAMPLES: Example[] = [
{
name: "UC Irvine",
geText: "GE IV - Arts and Humanities",
institution: "UCI",
ge: "IV",
link: "/search?uni=UCI&ge=GE%20IV",
},
{
name: "UCLA",
geText: "Life Sciences",
institution: "UCLA",
ge: "Scientific Inquiry: Life Sciences",
link: "search?uni=UCLA&ge=Scientific%20Inquiry%3A%20Life%20Sciences",
},
{
name: "UC Santa Barbara",
geText: "GE E - Culture and Thought",
institution: "UCSB",
ge: "E",
link: "/search?uni=UCSB&ge=GE%20E",
},
];

const ExampleCard = ({ example }: { example: Example }) => {
const [data, setData] = useState<DatabaseReturn>();

useEffect(() => {
const fetchData = async () => {
const courses = await queryDatabase(
example.institution,
example.ge,
);
setData(courses);
};
fetchData();
}, [example.name, example.ge]);

return (
<Link href={example.link} key={example.name + example.ge}>
<Card className="w-[275px] drop-shadow-md hover:shadow-md sm:w-[300px]">
<CardHeader>
<CardTitle>{example.name}</CardTitle>
<CardDescription>{example.geText}</CardDescription>
</CardHeader>
<CardFooter className="flex text-neutral-600">
<span className="font-bold text-primary">
<b>{data?.data.length ?? "..."}</b>
</span>
&nbsp;Courses&nbsp;
<span className="hidden sm:flex">Found</span>
</CardFooter>
</Card>
</Link>
);
};

const Examples = () => {
return (
<div className="flex flex-wrap gap-4 px-6 sm:justify-center lg:px-8">
{EXAMPLES.map((example) => (
<ExampleCard example={example} />
))}
</div>
);
};

export default Examples;
36 changes: 36 additions & 0 deletions components/hero/Graphics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Graphics = () => {
return (
<div>
<div className="relative isolate">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 -top-40 -z-10 transform-gpu blur-[52px] sm:-top-48"
>
<div
style={{
clipPath:
"polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
}}
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[26.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-blue-500 to-purple-500 opacity-30 sm:left-[calc(50%-30rem)] sm:w-[42.1875rem]"
/>
</div>
</div>
<div className="relative isolate hidden md:flex">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 -top-40 -z-10 transform-gpu blur-[52px] sm:-top-72"
>
<div
style={{
clipPath:
"polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)",
}}
className="relative aspect-[1155/678] w-[16.125rem] rotate-[30deg] bg-gradient-to-tr from-blue-500 to-purple-500 opacity-30 sm:w-[22.1875rem] md:-right-[0vw] lg:-right-[12vw] xl:-right-[20vw]"
/>
</div>
</div>
</div>
);
};

export default Graphics;
4 changes: 1 addition & 3 deletions components/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,7 @@ const Search = () => {

const fetchData = async () => {
try {
const universityParam = university;
const geParam = ge.includes("GE") ? ge.split(" ")[1] : ge;
const courses = await queryDatabase(universityParam, geParam);
const courses = await queryDatabase(university, ge);

setCourses(courses.data);
setLastUpdated(courses.lastUpdated);
Expand Down
13 changes: 8 additions & 5 deletions lib/utils/query-db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CourseObject } from "../../components/search/Search";

type DatabaseReturn = {
export type DatabaseReturn = {
data: CourseObject[];
lastUpdated: number;
};
Expand All @@ -11,7 +11,10 @@ export async function queryDatabase(
university: string,
ge: string,
): Promise<DatabaseReturn> {
const cacheKey = university + ge;
const universityParam = university;
const geParam = ge.includes("GE") ? ge.split(" ")[1] : ge;

const cacheKey = universityParam + geParam;

if (cache[cacheKey] && cache[cacheKey][0]) {
const [cachedDate, cachedData] = cache[cacheKey];
Expand All @@ -22,10 +25,10 @@ export async function queryDatabase(
}
}

const universityParam = encodeURIComponent(university);
const geParam = encodeURIComponent(ge);
const universityUri = encodeURIComponent(universityParam);
const geUri = encodeURIComponent(geParam);

const url = `https://ge-z.info/api/cvc-courses?institution=${universityParam}&ge=${geParam}`;
const url = `https://ge-z.info/api/cvc-courses?institution=${universityUri}&ge=${geUri}`;

try {
const response = await fetch(url);
Expand Down

0 comments on commit 665ecc9

Please sign in to comment.