feat: course-catalog-injected-popup (#98)
* some work * some work on course popup update the stories and create the header component * use chip component in header * complete CourseHeaderAndActions Component added course buttons, using proper subcomponents now. * Change test course to 314 * Add rmp callback * some unocss updates * add course button onclick handlers * add todo for calendar button * Rename CoursePopup Old one to "Old", remove "2" from new one * description stuff done * Modify story to use proper course info * Add Grade Distribution Stuff * Minor tweaks change style in header * Add TODO replace current grade colors with a tailwind palette * Fix syllabi url Remove unused variable and unnecessary args to url * Bunch of renaming * Kinda complete the handlers * change grade distribution colors to match updated figma * change from reducer pattern to state variables, remove chartData from state * add additional story * disabled add when course is not open * use array fill * Some changes with the instructor names * trying to get the CES stuff to work * CES button is working * remove a todo * add actual color for dminus * fix description, start no distribution state * post merge fixes * small fixes * fix: import as type * fix: some better typescript stuff i think * fix: manifest.ts * fix: pr feedback * Apply suggestions from code review --------- Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,32 @@
|
||||
import type { Course } from '@shared/types/Course';
|
||||
import Spinner from '@views/components/common/Spinner/Spinner';
|
||||
import Text from '@views/components/common/Text/Text';
|
||||
import { CourseCatalogScraper } from '@views/lib/CourseCatalogScraper';
|
||||
import { SiteSupport } from '@views/lib/getSiteSupport';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
interface DescriptionProps {
|
||||
lines: string[];
|
||||
course: Course;
|
||||
}
|
||||
|
||||
const LoadStatus = {
|
||||
LOADING: 'LOADING',
|
||||
DONE: 'DONE',
|
||||
ERROR: 'ERROR',
|
||||
} as const;
|
||||
type LoadStatusType = (typeof LoadStatus)[keyof typeof LoadStatus];
|
||||
|
||||
async function fetchDescription(course: Course): Promise<string[]> {
|
||||
if (!course.description?.length) {
|
||||
const response = await fetch(course.url);
|
||||
const text = await response.text();
|
||||
const doc = new DOMParser().parseFromString(text, 'text/html');
|
||||
|
||||
const scraper = new CourseCatalogScraper(SiteSupport.COURSE_CATALOG_DETAILS);
|
||||
course.description = scraper.getDescription(doc);
|
||||
}
|
||||
return course.description;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -11,27 +34,53 @@ interface DescriptionProps {
|
||||
*
|
||||
* @component
|
||||
* @param {DescriptionProps} props - The component props.
|
||||
* @param {string[]} props.lines - The lines of text to render.
|
||||
* @param {Course} props.course - The course for which to display the description.
|
||||
* @returns {JSX.Element} The rendered description component.
|
||||
*/
|
||||
const Description: React.FC<DescriptionProps> = ({ lines }: DescriptionProps) => {
|
||||
const Description: React.FC<DescriptionProps> = ({ course }: DescriptionProps) => {
|
||||
const [description, setDescription] = React.useState<string[]>([]);
|
||||
const [status, setStatus] = React.useState<LoadStatusType>(LoadStatus.LOADING);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchDescription(course)
|
||||
.then(description => {
|
||||
setStatus(LoadStatus.DONE);
|
||||
setDescription(description);
|
||||
})
|
||||
.catch(() => {
|
||||
setStatus(LoadStatus.ERROR);
|
||||
});
|
||||
}, [course]);
|
||||
|
||||
const keywords = ['prerequisite', 'restricted'];
|
||||
return (
|
||||
<ul className='my-[5px] space-y-1.5 children:marker:text-ut-burntorange'>
|
||||
{lines.map(line => {
|
||||
const isKeywordPresent = keywords.some(keyword => line.toLowerCase().includes(keyword));
|
||||
return (
|
||||
<div className='flex gap-2'>
|
||||
<span className='text-ut-burntorange'>•</span>
|
||||
<li key={line}>
|
||||
<Text variant='p' className={clsx({ 'font-bold text-ut-burntorange': isKeywordPresent })}>
|
||||
{line}
|
||||
</Text>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<>
|
||||
{status === LoadStatus.ERROR && (
|
||||
<Text color='theme-red'>Please refresh the page and log back in using your UT EID and password</Text>
|
||||
)}
|
||||
{/* TODO (achadaga): would be nice to have a new spinner here */}
|
||||
{status === LoadStatus.LOADING && <Spinner />}
|
||||
{status === LoadStatus.DONE && (
|
||||
<ul className='my-[5px] space-y-1.5 children:marker:text-ut-burntorange'>
|
||||
{description.map(line => {
|
||||
const isKeywordPresent = keywords.some(keyword => line.toLowerCase().includes(keyword));
|
||||
return (
|
||||
<div key={line} className='flex gap-2'>
|
||||
<span className='text-ut-burntorange'>•</span>
|
||||
<li key={line}>
|
||||
<Text
|
||||
variant='p'
|
||||
className={clsx({ 'font-bold text-ut-burntorange': isKeywordPresent })}
|
||||
>
|
||||
{line}
|
||||
</Text>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user