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

[Improve] made pagination buttons larger for small screens #99

97 changes: 64 additions & 33 deletions app/(main)/compare/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Card, LinkBtn } from '@/components/atoms';
import { Btn, Card, LinkBtn } from '@/components/atoms';
import { CompareItem, FilterGroup, SearchBar } from '@/components/molecules';
import { BarChart } from '@/components/organisms';
import {
Expand All @@ -8,6 +8,7 @@ import {
} from '@/types';
import fetcher from '@/utils/fetcher';
import roundToTenth from '@/utils/round-to-tenth';
import { EllipsisVerticalIcon, XMarkIcon } from '@heroicons/react/24/solid';

export default async function Page({
searchParams,
Expand Down Expand Up @@ -108,12 +109,34 @@ export default async function Page({

return (
<main>
<section className="mx-auto flex w-full max-w-content-width items-stretch gap-md px-md">
<div className="w-[250px] max-lg:hidden">
<div className="sticky top-0 flex max-h-[100dvh] min-h-[50dvh] w-full flex-col gap-sm overflow-y-auto pb-lg pt-lg">
<p className="pb-md">
Search and select professors and/or courses to compare
</p>
<div className="mx-auto flex w-full max-w-content-width px-md py-lg">
<p className="flex-1">Grade/Rating Analysis</p>
<Btn
popoverTarget="filters"
variant="tertiary"
className="rounded-sm p-0 lg:hidden"
>
<EllipsisVerticalIcon width={24} height={24} />
</Btn>
</div>
<section className="mx-auto flex w-full max-w-content-width items-stretch px-md">
<div className="lg:w-[250px] lg:pr-md">
<div
id="filters"
popover="auto"
className="top-0 max-h-[100dvh] min-h-[50dvh] w-full overflow-y-auto bg-page pb-lg pt-lg max-lg:h-[100dvh] max-lg:px-md lg:sticky lg:flex lg:flex-col lg:gap-sm"
>
<div className="flex pb-md">
<p className="flex-1">Filters</p>
<Btn
popoverTarget="filters"
variant="tertiary"
className="rounded-sm p-0 lg:hidden"
>
<XMarkIcon width={24} height={24} />
</Btn>
</div>

<div className="pb-lg pr-md">
<SearchBar param="compareQuery" shouldResetPageOnChange={false} />
</div>
Expand Down Expand Up @@ -149,41 +172,49 @@ export default async function Page({
</div>
</div>
<div className="flex min-w-0 flex-1 flex-col items-stretch gap-md pb-lg pt-lg">
<div className="flex gap-sm overflow-x-auto">
{professorStats.map((professor) => (
<CompareItem
key={professor.id}
link={`/professors/${professor.id}`}
review={professor.review}
takeAgainPercent={professor.takeAgainPercent}
avgGrade={professor.avgGrade}
totalReviews={professor.totalReviews}
id={professor.id}
/>
))}
{courseStats.map((course) => (
<CompareItem
key={course.id}
link={`/courses/${course.id}`}
review={course.review}
takeAgainPercent={course.takeAgainPercent}
avgGrade={course.avgGrade}
totalReviews={course.totalReviews}
id={course.id}
/>
))}
</div>
{professorStats.length + courseStats.length ? (
<div className="flex gap-sm overflow-x-auto">
{professorStats.map((professor) => (
<CompareItem
key={professor.id}
link={`/professors/${professor.id}`}
review={professor.review}
takeAgainPercent={professor.takeAgainPercent}
avgGrade={professor.avgGrade}
totalReviews={professor.totalReviews}
id={professor.id}
/>
))}
{courseStats.map((course) => (
<CompareItem
key={course.id}
link={`/courses/${course.id}`}
review={course.review}
takeAgainPercent={course.takeAgainPercent}
avgGrade={course.avgGrade}
totalReviews={course.totalReviews}
id={course.id}
/>
))}
</div>
) : (
<p className="w-full p-lg text-center italic text-neutral">
No items selected.
<br />
Search and select professors/courses to compare.
</p>
)}
<Card className="p-lg max-lg:w-full lg:flex-1">
<p className="pb-sm font-bold">Rating Distribution</p>
<BarChart
series={[
...(professorStats.map((professor) => ({
name: professor.id,
data: professor.ratingDistribution,
data: professor.ratingDistribution.reverse(),
})) ?? []),
...(courseStats.map((course) => ({
name: course.id,
data: course.ratingDistribution,
data: course.ratingDistribution.reverse(),
})) ?? []),
]}
categories={[5, 4, 3, 2, 1]}
Expand Down
2 changes: 1 addition & 1 deletion app/(main)/courses/[id]/@schedules/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const getKey =
(pageIndex: number, previousPageData: CoursesIDSchedulesResponse) => {
if (previousPageData && previousPageData.page === previousPageData.pages)
return null;
return `/django/core/courses/${id}/schedules?page=${pageIndex + 1}`;
return `/django/core/courses/${id}/schedules?limit=4&page=${pageIndex + 1}`;
};

export default function Page({ params }: { params: { id: string } }) {
Expand Down
7 changes: 6 additions & 1 deletion app/(main)/courses/[id]/@statistics/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,12 @@ export default async function Page({
<Card className="p-lg max-lg:w-full lg:flex-1">
<p className="pb-sm font-bold">Rating Distribution</p>
<BarChart
series={[{ name: 'Rating Distribution', data: reviewDistribution }]}
series={[
{
name: 'Rating Distribution',
data: reviewDistribution.reverse(),
},
]}
categories={[5, 4, 3, 2, 1]}
/>
</Card>
Expand Down
2 changes: 1 addition & 1 deletion app/(main)/professors/[id]/@schedules/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const getKey =
(pageIndex: number, previousPageData: ProfessorsIDSchedulesResponse) => {
if (previousPageData && previousPageData.page === previousPageData.pages)
return null;
return `/django/core/professors/${id}/schedules?page=${pageIndex + 1}`;
return `/django/core/professors/${id}/schedules?limit=4&page=${pageIndex + 1}`;
};

export default function Page({ params }: { params: { id: string } }) {
Expand Down
2 changes: 1 addition & 1 deletion app/(main)/professors/[id]/@statistics/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ export default async function Page({
series={[
{
name: 'Rating Distribution',
data: reviewDistribution,
data: reviewDistribution.reverse(),
},
]}
categories={[5, 4, 3, 2, 1]}
Expand Down
153 changes: 153 additions & 0 deletions components/molecules/client/compare-search-bar/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
'use client';

import { useSearchParams } from 'next/navigation';
import useSWR from 'swr';

import { Card } from '@/components/atoms';
import { ParamSelect, SearchBar } from '@/components/molecules';
import { CoursesSearchResponse, ProfessorsSearchResponse } from '@/types';
import SWRConfigProvider from '@/wrappers/swr-config';
import Link from 'next/link';

type Error = { message: string };

const useCoursesSearchResults = (currentQuery: string) => {
const apiQueryParams = new URLSearchParams();
apiQueryParams.set('query', currentQuery);
apiQueryParams.set('limit', '3');
const { data, error, isLoading } = useSWR<CoursesSearchResponse, Error>(
`/django/core/courses/search?${apiQueryParams.toString()}`,
);
return { data, error, isLoading };
};

const useProfessorsSearchResults = (currentQuery: string) => {
const apiQueryParams = new URLSearchParams();
apiQueryParams.set('query', currentQuery);
apiQueryParams.set('limit', '3');

const { data, error, isLoading } = useSWR<ProfessorsSearchResponse, Error>(
`/django/core/professors/search?${apiQueryParams.toString()}`,
);
return { data, error, isLoading };
};

interface StatusMessageProps {
isLoading: boolean;
error: Error | undefined;
data: ({ total_results: number } & unknown) | undefined;
}

const StatusMessage: React.FC<StatusMessageProps> = ({
isLoading,
error,
data,
}) => {
if (isLoading) {
return <li className="mx-auto my-8 w-fit">Loading...</li>;
}
if (error) {
return (
<li className="mx-auto my-8 w-fit text-important">
Error: {error.message}
</li>
);
}
if (!data || data.total_results === 0) {
return <li className="mx-auto my-8 w-fit">No results found</li>;
}
return null;
};

const CourseSearchResults: React.FC = () => {
const searchParams = useSearchParams();
const currentOption = searchParams.get('compareOption') ?? 'professors';
const currentQuery = searchParams.get('compareQuery') ?? '';
const { data, error, isLoading } = useCoursesSearchResults(currentQuery);
return (
<menu>
<StatusMessage isLoading={isLoading} error={error} data={data} />
{data && data.total_results > 0
? data.items.map((course, i) => (
<li key={i} className="border-b-2 border-border last:border-b-0">
<Link
href={`/courses/${course.department}-${course.course_number}?navOption=${currentOption}`}
className="flex flex-col px-lg py-md animation hover:bg-[rgb(var(--color-primary)/0.15)] focus:bg-[rgb(var(--color-primary)/0.15)]"
>
<span className="overflow-ellipsis text-small-lg text-neutral">
{course.department} {course.course_number}
</span>
<span className="overflow-ellipsis text-p font-bold">
{course.name}
</span>
</Link>
</li>
))
: null}
</menu>
);
};

const ProfessorSearchResults: React.FC = () => {
const searchParams = useSearchParams();
const currentOption = searchParams.get('compareOption') ?? 'professors';
const currentQuery = searchParams.get('compareQuery') ?? '';
const { data, error, isLoading } = useProfessorsSearchResults(currentQuery);
return (
<menu>
<StatusMessage isLoading={isLoading} error={error} data={data} />
{data && data.total_results > 0
? data.items.map((professor, i) => (
<li key={i} className="border-b-2 border-border last:border-b-0">
<Link
href={`/professors/${professor.email.split('@')[0]}?navOption=${currentOption}`}
className="flex flex-col px-lg py-md animation hover:bg-[rgb(var(--color-primary)/0.15)] focus:bg-[rgb(var(--color-primary)/0.15)]"
>
<span className="overflow-ellipsis text-small-lg text-neutral">
{professor.email}
</span>
<span className="overflow-ellipsis text-p font-bold">
{professor.name}
</span>
</Link>
</li>
))
: null}
</menu>
);
};

export const CompareSearchBar: React.FC = () => {
const searchParams = useSearchParams();
const currentOption = searchParams.get('compareOption') ?? 'professors';
const currentQuery = searchParams.get('compareQuery') ?? '';
return (
<div className="relative flex whitespace-nowrap">
<SearchBar
param="compareQuery"
shouldResetPageOnChange={false}
className="peer flex-1 [&>input]:!rounded-r-none"
/>
<ParamSelect
param="compareOption"
shouldResetPageOnChange={false}
className="rounded-l-none border-border bg-background"
value={currentOption}
>
<option value="professors">Professors</option>
<option value="courses">Courses</option>
</ParamSelect>
{currentQuery ? (
<Card className="absolute left-0 top-[50px] z-50 hidden w-[1000px] max-w-[80dvw] shadow-paper hover:block peer-focus-within:block peer-has-[:placeholder-shown]:hidden">
<SWRConfigProvider>
{currentOption === 'professors' ? (
<ProfessorSearchResults />
) : currentOption === 'courses' ? (
<CourseSearchResults />
) : null}
</SWRConfigProvider>
</Card>
) : null}
</div>
);
};
2 changes: 2 additions & 0 deletions components/molecules/client/compare-search-bar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { CompareSearchBar as default } from './component';
export * from './component';
1 change: 1 addition & 0 deletions components/molecules/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './profile-btn';
export * from './nav-search-bar';
export * from './pagination-bar';
export * from './color-mode-picker';
export * from './compare-search-bar';
4 changes: 2 additions & 2 deletions components/molecules/client/pagination-bar/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function PaginationNumber({
isActive: boolean;
}) {
const className = cn(
'flex max-lg:h-6 max-lg:min-w-[24px] w-fit px-1 lg:h-10 lg:min-w-[40px] items-center justify-center max-lg:text-small-sm border-border border-2 transition-all duration-100 ease-in-out',
'flex max-lg:h-8 max-lg:min-w-8 w-fit px-1 lg:h-10 lg:min-w-10 items-center justify-center max-lg:text-small-sm border-border border-2 transition-all duration-100 ease-in-out',
{
'rounded-l-sm': position === 'first' || position === 'single',
'rounded-r-sm': position === 'last' || position === 'single',
Expand Down Expand Up @@ -68,7 +68,7 @@ const PaginationArrow = ({
isDisabled?: boolean;
}) => {
const className = cn(
'flex max-lg:h-6 max-lg:w-6 lg:h-10 lg:w-10 items-center justify-center rounded-sm transition-all duration-100 ease-in-out',
'flex max-lg:h-8 max-lg:w-8 lg:h-10 lg:w-10 items-center justify-center rounded-sm transition-all duration-100 ease-in-out',
{
'pointer-events-none text-neutral': isDisabled,
'hover:bg-border': !isDisabled,
Expand Down
5 changes: 3 additions & 2 deletions components/molecules/client/profile-btn/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { LinkBtn } from '@/components/atoms';
import { cn } from '@/utils/cn';
import { useSession } from '@/wrappers/session-provider';
import { UserIcon } from '@heroicons/react/20/solid';
import { UserCircleIcon } from '@heroicons/react/24/solid';

interface Props
extends Omit<
Expand All @@ -20,8 +20,9 @@ export const ProfileBtn: React.FC<Props> = ({ className, ...props }) => {
{...props}
href="/profile"
variant="tertiary"
aria-label="Profile"
>
<UserIcon width={20} height={20} /> Profile
<UserCircleIcon width={24} height={24} />{' '}
</LinkBtn>
);
}
Expand Down
Loading
Loading