Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor landing page, make example cards fetch current data #56

Merged
merged 1 commit into from
Apr 12, 2024
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
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
Loading