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
19 changes: 18 additions & 1 deletion src/components/CourseInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ErrorWithFields, softError } from '../../log';
import { getSemesterName } from '../../utils/semesters';
import ProfessorInfoCard from '../ProfessorInfoCard';
import Button from '../Button';
import { MajorRestrictionsView } from '../MajorRestrictionsView';

import './stylesheet.scss';

Expand Down Expand Up @@ -73,7 +74,6 @@ function CourseHeader({
);
}

// Need to create course info short and course info long
export default function CourseInfo({
courseId,
enableTermSelect = false,
Expand All @@ -89,6 +89,9 @@ export default function CourseInfo({
useState(false);
const [selectedTermKey, setSelectedTermKey] = useState<string | null>(null);
const [gpaMap, setGpaMap] = useState<CourseGpa | null>(null);
const [restrictionsViewMajors, setRestrictionsViewMajors] = React.useState<
string[] | null
>(null);

useEffect(() => {
if (course) {
Expand Down Expand Up @@ -254,6 +257,17 @@ export default function CourseInfo({
return <div />;
}

if (restrictionsViewMajors !== null) {
return (
<MajorRestrictionsView
majors={restrictionsViewMajors}
onBack={(): void => setRestrictionsViewMajors(null)}
onHide={onHide}
courseName={`${course.subject} ${course.number}`}
/>
);
}

return (
<div className="course-info-container">
<div className="course-info-content">
Expand Down Expand Up @@ -323,6 +337,9 @@ export default function CourseInfo({
isRatingsLoaded={isProfessorRatingsLoaded}
course={course}
displaySectionInfo={selectedTermKey === term}
onViewRestrictions={
isModal ? setRestrictionsViewMajors : undefined
}
/>
))}
</div>
Expand Down
75 changes: 75 additions & 0 deletions src/components/MajorRestrictionsView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import {
faChevronLeft,
faChevronRight,
faXmark,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import './stylesheet.scss';

export type MajorRestrictionsViewProps = {
majors: string[];
onBack?: () => void;
onHide?: () => void;
courseName?: string;
};

export function MajorRestrictionsView({
majors,
onBack,
onHide,
courseName = undefined,
}: MajorRestrictionsViewProps): React.ReactElement {
return (
<div className="modal-restrictions-body">
<div className="modal-header-custom">
<div className="modal-header-back-title-container">
{onBack && (
<div
className="back-icon"
onClick={onBack}
role="button"
tabIndex={0}
>
<FontAwesomeIcon icon={faChevronLeft} size="lg" />
</div>
)}
<h3>Major Restrictions</h3>
</div>

<div
className="close-x-icon"
onClick={onHide}
role="button"
tabIndex={0}
aria-label="Close modal"
>
<FontAwesomeIcon icon={faXmark} size="lg" />
</div>
</div>

{onBack && courseName && (
<div className="modal-section-details-container">
<div className="modal-course-name" onClick={onBack}>
{courseName} Section Details
</div>
<div className="modal-section-chevron">
<FontAwesomeIcon icon={faChevronRight} />
</div>
Major Restrictions
</div>
)}

<p>
<strong>Must be enrolled in one of the following Majors:</strong>
</p>

<ul className="no-bullet-list">
{majors.map((major, i) => (
<li key={i}>{major}</li>
))}
</ul>
</div>
);
}
82 changes: 82 additions & 0 deletions src/components/MajorRestrictionsView/stylesheet.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@import '../../variables';

.modal-restrictions-body {
text-align: left;
padding: 4px;
padding-bottom: 40px;

.modal-header-custom {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 48px;

.modal-header-back-title-container {
display: flex;
align-items: center;
gap: 20px;

h3 {
font-size: 20px;
margin: 0;
font-weight: 400;
}
}

.close-x-icon,
.back-icon {
color: #666666;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;

&:hover,
&:focus {
@include dark(color, $theme-light-background);
@include light(color, #000);
outline: none;
}
}
}

.modal-section-details-container {
display: flex;
align-items: center;
gap: 12px;
padding-bottom: 20px;

.modal-course-name {
color: $information-blue;
cursor: pointer;
font-size: 16px;
}

.modal-section-chevron {
width: 6px;
height: 11px;
display: flex;
align-items: center;
justify-content: center;
}
}

p {
margin-bottom: 20px;
margin-top: 24px;
font-size: 14px;
}

.no-bullet-list {
list-style-type: none;
margin: 0;
max-height: 700px;
overflow-y: auto;
padding-left: 40px;

li {
font-size: 14px;
border-bottom: none;
}
}
}
52 changes: 52 additions & 0 deletions src/components/ProfessorInfoCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import ActionRow from '../ActionRow';
import MetricsCard, { Metric } from '../MetricsCard';
import useScrollFade from '../../hooks/useScrollFade';
import { OccupiedInfo } from '../../types';
import Modal from '../Modal';
import { MajorRestrictionsView } from '../MajorRestrictionsView';

import './stylesheet.scss';

Expand All @@ -29,6 +31,7 @@ export type ProfessorInfoCardProps = {
isRatingsLoaded: boolean;
course: Course;
displaySectionInfo: boolean;
onViewRestrictions?: (majors: string[]) => void;
};

function SectionRow({
Expand All @@ -37,14 +40,24 @@ function SectionRow({
isPinned,
onAdd,
onRemove,
onViewRestrictions,
}: {
section: Section;
term: string;
isPinned: boolean;
onAdd: () => void;
onRemove: () => void;
onViewRestrictions?: (majors: string[]) => void;
}): React.ReactElement {
const meeting = section.meetings[0];
const [isModalOpen, setIsModalOpen] = useState(false);
const majors =
section.restrictions?.allowed
?.filter((res) => res.category === 'Majors')
.map((res) => res.value) || [];
const majorString = majors.join(', ') || 'None';
// Only show the link if there are multiple majors or a long string
const needsViewAll = majors.length > 1 || majorString.length > 25;

const [isLoaded, setIsLoaded] = useState(false);

Expand Down Expand Up @@ -82,6 +95,14 @@ function SectionRow({
return `${info.occupied} / ${info.total}`;
};

const handleViewAllClick = (): void => {
if (onViewRestrictions) {
onViewRestrictions(majors);
} else {
setIsModalOpen(true);
}
};

if (!section) {
return <div />;
}
Expand Down Expand Up @@ -114,6 +135,34 @@ function SectionRow({
{formatSeatData(seating?.waitlist ?? null)}
</div>
<div className="row-cell">{meeting?.where || 'TBA'}</div>
<div className="row-cell restrictions-cell">
<div className="restrictions-text" title={majorString}>
{majorString}
</div>

{needsViewAll && (
<div
className="view-all-link"
onClick={handleViewAllClick}
role="button"
tabIndex={0}
>
View all
</div>
)}

<Modal
show={isModalOpen}
onHide={(): void => setIsModalOpen(false)}
width={800}
className="major-restrictions-modal"
>
<MajorRestrictionsView
majors={majors}
onHide={(): void => setIsModalOpen(false)}
/>
</Modal>
</div>
</div>
</ActionRow>
);
Expand All @@ -126,6 +175,7 @@ export default function ProfessorInfoCard({
isRatingsLoaded,
course,
displaySectionInfo,
onViewRestrictions,
}: ProfessorInfoCardProps): React.ReactElement {
const scrollRef = React.useRef<HTMLDivElement>(null);
const { fadeLeft, fadeRight } = useScrollFade(scrollRef, displaySectionInfo);
Expand Down Expand Up @@ -212,6 +262,7 @@ export default function ProfessorInfoCard({
<div className="row-cell">Seats Filled</div>
<div className="row-cell">Waitlist</div>
<div className="row-cell">Location</div>
<div className="row-cell">Major Lock</div>
</div>
</div>

Expand All @@ -224,6 +275,7 @@ export default function ProfessorInfoCard({
isPinned={pinnedCrns.includes(section.crn)}
onAdd={(): void => handleAddSection(section)}
onRemove={(): void => handleRemoveSection(section)}
onViewRestrictions={onViewRestrictions}
/>
);
})}
Expand Down
24 changes: 23 additions & 1 deletion src/components/ProfessorInfoCard/stylesheet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
150px /* Time */
115px /* Seats Filled */
103px /* Waitlist */
210px; /* Location */
210px /* Location */
210px; /* Major Lock */
align-items: center;
}

Expand Down Expand Up @@ -91,6 +92,7 @@
display: flex;
align-items: center;
min-height: 46px;
min-width: 0;

.action-button {
background: none;
Expand All @@ -105,6 +107,26 @@
color: $information-blue;
}
}

&.restrictions-cell {
gap: 6px;

.restrictions-text {
flex: 1 1 0%;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.view-all-link {
flex-shrink: 0;
color: $information-blue;
cursor: pointer;
font-size: 12px;
white-space: nowrap;
}
}
}
}
}
Expand Down
Loading
Loading