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
5 changes: 3 additions & 2 deletions site/src/component/ResultPageContent/ResultPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import Twemoji from 'react-twemoji';

interface ResultPageSectionProps {
title: string;
id?: string;
children: ReactNode;
}

export const ResultPageSection: FC<ResultPageSectionProps> = ({ title, children }) => {
export const ResultPageSection: FC<ResultPageSectionProps> = ({ title, id, children }) => {
return (
<div className="result-page-section">
<div className="result-page-section" id={id}>
<h2>{title}</h2>
{children}
</div>
Expand Down
10 changes: 6 additions & 4 deletions site/src/component/ResultPreview/CoursePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useAppDispatch } from '../../store/hooks';
import { useCourseData } from '../../hooks/catalog';
import { setToastMsg, setToastSeverity, setShowToast } from '../../store/slices/roadmapSlice';
import Twemoji from 'react-twemoji';
import PreviewNavBar from './PreviewNavBar';

import CloseIcon from '@mui/icons-material/Close';
import BackIcon from '@mui/icons-material/ArrowBack';
Expand Down Expand Up @@ -53,23 +54,23 @@ const CoursePreviewContent: FC<{ data: CourseGQLData }> = ({ data }) => {

return (
<div className="preview-body">
<ResultPageSection title={data.title}>
<ResultPageSection id="preview-details" title={data.title}>
<CourseSummary course={data} />
</ResultPageSection>

<ResultPageSection title="📊 Grade Distribution">
<ResultPageSection id="preview-grades" title="📊 Grade Distribution">
<GradeDist course={data} />
</ResultPageSection>

<ResultPageSection title="🗓️ Schedule of Classes">
<ResultPageSection id="preview-schedule" title="🗓️ Schedule of Classes">
<Schedule
key={data.id}
courseID={data.department + ' ' + data.courseNumber}
termsOffered={sortTerms(data.terms)}
/>
</ResultPageSection>

<ResultPageSection title="💬 Reviews">
<ResultPageSection id="preview-reviews" title="💬 Reviews">
<Review key={data.id} course={data} terms={sortTerms(data.terms)} />
</ResultPageSection>
</div>
Expand Down Expand Up @@ -124,6 +125,7 @@ const CoursePreview: FC<{ courseId: string; onClose: () => void; onBack: () => v
</Tooltip>
)}
<PreviewTitle isLoading={isLoading} courseId={courseId} courseData={courseData} />
<PreviewNavBar />
<Button
variant="contained"
color="inherit"
Expand Down
66 changes: 66 additions & 0 deletions site/src/component/ResultPreview/PreviewNavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useEffect, useState } from 'react';
import { Button } from '@mui/material';
import ArticleOutlinedIcon from '@mui/icons-material/ArticleOutlined';
import BarChartIcon from '@mui/icons-material/BarChart';
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
import RateReviewIcon from '@mui/icons-material/RateReview';

const previewLinks = [
{ id: 'preview-details', label: 'Details', icon: <ArticleOutlinedIcon /> },
{ id: 'preview-grades', label: 'Grades', icon: <BarChartIcon /> },
{ id: 'preview-schedule', label: 'Schedule', icon: <CalendarTodayIcon /> },
{ id: 'preview-reviews', label: 'Reviews', icon: <RateReviewIcon /> },
];

const PreviewNavBar = () => {
const [activeSection, setActiveSection] = useState(previewLinks[0].id);

useEffect(() => {
const scrollContainer = document.querySelector('.result-preview > div:last-child');
if (!scrollContainer) return;

const updateActiveSection = () => {
const containerTop = scrollContainer.getBoundingClientRect().top;
let currentSection = previewLinks[0];

for (let i = previewLinks.length - 1; i >= 0; i--) {
const section = document.getElementById(previewLinks[i].id);
if (section && section.getBoundingClientRect().top <= containerTop + 40) {
currentSection = previewLinks[i];
break;
}
}

setActiveSection(currentSection.id);
};

updateActiveSection();
scrollContainer.addEventListener('scroll', updateActiveSection);
return () => scrollContainer.removeEventListener('scroll', updateActiveSection);
}, []);

const scrollToSection = (id: string) => {
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
};

return (
<nav className="preview-nav-bar" aria-label="Preview sections">
{previewLinks.map((link) => (
<Button
className={`preview-nav-button ${activeSection === link.id ? 'active' : ''}`}
color="inherit"
key={link.id}
onClick={() => scrollToSection(link.id)}
size="small"
startIcon={link.icon}
variant="contained"
disableRipple={true}
>
<span>{link.label}</span>
</Button>
))}
</nav>
);
};

export default PreviewNavBar;
9 changes: 6 additions & 3 deletions site/src/component/ResultPreview/ProfessorPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import IosShareIcon from '@mui/icons-material/IosShare';
import { getProfessorTerms } from '../../helpers/reviews';
import SideInfo from '../SideInfo/SideInfo';
import { useProfessorData } from '../../hooks/professorReviews';
import PreviewNavBar from './PreviewNavBar';

interface PreviewTitleProps {
isLoading: boolean;
Expand Down Expand Up @@ -48,6 +49,7 @@ const ProfessorPreviewContent: FC<{ data: ProfessorGQLData | null }> = ({ data }
return (
<div className="preview-body">
<SideInfo
id="preview-details"
className="professor-summary"
searchType="instructor"
name={data.name}
Expand All @@ -57,15 +59,15 @@ const ProfessorPreviewContent: FC<{ data: ProfessorGQLData | null }> = ({ data }
professor={data}
/>

<ResultPageSection title="📊 Grade Distribution">
<ResultPageSection id="preview-grades" title="📊 Grade Distribution">
<GradeDist professor={data} />
</ResultPageSection>

<ResultPageSection title="🗓️ Schedule of Classes">
<ResultPageSection id="preview-schedule" title="🗓️ Schedule of Classes">
<Schedule professorIDs={data.shortenedNames} termsOffered={unionTerms(data.courses)} />
</ResultPageSection>

<ResultPageSection title="💬 Reviews">
<ResultPageSection id="preview-reviews" title="💬 Reviews">
<Review professor={data} terms={sortTerms(getProfessorTerms(data))} />
</ResultPageSection>
</div>
Expand Down Expand Up @@ -120,6 +122,7 @@ const ProfessorPreview: FC<{ netid: string; onClose: () => void; onBack: () => v
</Tooltip>
)}
<PreviewTitle isLoading={isLoading} netId={netid} professorData={professorData} />
<PreviewNavBar />
<Button
variant="contained"
color="inherit"
Expand Down
90 changes: 88 additions & 2 deletions site/src/component/ResultPreview/ResultPreview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
z-index: 10;
background-color: var(--mui-palette-background-default);
overscroll-behavior: contain;
container-type: inline-size;

.result-page-section {
margin-inline: auto;
Expand All @@ -24,7 +25,7 @@
top: 0;
left: 0;
width: 100%;
min-height: globals.$roadmap-toolbar-height;
height: globals.$roadmap-toolbar-height;
border-width: 0 0 2px 0;
border-radius: 0;
display: flex;
Expand All @@ -39,7 +40,32 @@

.preview-title {
margin-block: 0;
margin-right: auto;
}

.preview-nav-bar {
display: flex;
gap: 7px;
margin-inline: auto;
padding: 5px;
border: 1px solid var(--border-primary);
border-radius: 8px;
background-color: var(--mui-palette-Button-inheritContainedBg);
}

.preview-nav-button.MuiButton-root {
margin: 0;
border-radius: 8px;
background-color: var(--mui-palette-background-paper);
font-weight: 700;
text-transform: capitalize;

&:hover {
background-color: var(--mui-palette-background-paper);
}

&.active {
color: var(--mui-palette-secondary-main);
}
}

// Target the Twemoji wrapper since we can't add a class name to it
Expand All @@ -58,4 +84,64 @@
max-width: 1000px;
margin-bottom: 32px;
}

@container (max-width: 940px) {
.preview-title {
margin-right: auto;
}

.preview-nav-bar {
position: fixed;
left: 50%;
transform: translateX(-50%);
border-radius: 999px;
z-index: 999;
}

.preview-nav-button.MuiButton-root {
border-radius: 999px;

.MuiButton-startIcon {
margin: 0;
}

> span:not(.MuiButton-icon) {
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
}
}
@container (max-width: 433px) {
.preview-nav-bar {
right: auto;
bottom: 20px;
padding: 4px;
gap: 4px;
}

.preview-nav-button.MuiButton-root {
min-width: 40px;
padding: 10px;
}
}

@container (min-width: 434px) and (max-width: 940px) {
.preview-nav-bar {
right: auto;
bottom: 30px;
padding: 6px;
gap: 6px;
@media (min-width: 802px) {
// layout expands to preview and sidebar
left: calc((100vw - 367px) / 2 + 367px); // center based on preview container width only
}
}

.preview-nav-button.MuiButton-root {
min-width: 45px;
padding: 12px;
}
}
}
}
3 changes: 2 additions & 1 deletion site/src/component/SideInfo/SideInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ interface SideInfoProps {
professor?: ProfessorGQLData;
terms?: string[];
className?: string;
id?: string;
}

interface AverageReview {
Expand Down Expand Up @@ -169,7 +170,7 @@ const SideInfo: FC<SideInfoProps> = (props) => {

return (
<div className="side-content-wrapper">
<div className={`side-info ${props.className ?? ''}`}>
<div className={`side-info ${props.className ?? ''}`} id={props.id}>
<div className="side-info-overview">
<h2>{props.name}</h2>
<h3>{props.title}</h3>
Expand Down
Loading