Skip to content
Merged
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
70 changes: 59 additions & 11 deletions src/components/CourseDetailPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Course } from '@/types/course';
import { useEffect, useRef } from 'react';

interface Props {
course: Course | null;
Expand All @@ -16,15 +17,48 @@ interface Props {
// shell. Rendering the actual course fields and styling is in scope for the ticket.

export default function CourseDetailPanel({ course, onClose }: Props) {
const panelRef = useRef<HTMLElement>(null);
const prevFocusRef = useRef<HTMLElement | null>(null);

useEffect(() => {
if (course) {
prevFocusRef.current = document.activeElement as HTMLElement;
panelRef.current?.focus();
} else {
prevFocusRef.current?.focus();
prevFocusRef.current = null;
}
}, [course]);

useEffect(() => {
const handleKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};

document.addEventListener('keydown', handleKey);
return () => document.removeEventListener('keydown', handleKey);
}, [onClose]);

if (course === null) return null;

return (
<aside
className="absolute inset-y-0 right-0 z-10 w-80 overflow-y-auto border-l border-gray-200 bg-white p-4 shadow-lg"
className="absolute inset-y-0 right-0 z-10 w-90 max-sm:w-5/6 overflow-y-auto border-l border-gray-200 bg-white p-4 shadow-lg"
aria-label={`Details for ${course.code}`}
ref={panelRef}
tabIndex={-1}
>
<div className="flex items-start justify-between gap-2">
<h2 className="text-lg font-bold text-gray-900">{course.code}</h2>
<div className="flex items-start justify-between gap-2 border-b border-gray-200 pb-3">
<div>
<h2 className="text-xl font-bold text-red-600">
{course.code} [{course.credits} credit]
</h2>
<p className="mt-1 text-base font-semibold text-gray-900">
{course.title}
</p>
</div>
<button
type="button"
onClick={onClose}
Expand All @@ -35,14 +69,28 @@ export default function CourseDetailPanel({ course, onClose }: Props) {
</button>
</div>

{/*
TODO(volunteer) — flesh out the panel body. Fields available on `course`:
title, credits, description, prereqRaw, precludes.
Prereq: render `course.prereqRaw` as plain text for now (a richer view of
the parsed `course.prereq` AST is a later enhancement).
Also: visual design, Escape-to-close + focus management, mobile treatment
See the course-detail-panel ticket.
*/}
<div className="space-y-3 mt-4">
{course.description && (
<div>
<h3 className="font-semibold">Description</h3>
<p>{course.description}</p>
</div>
)}

{course.precludes && course.precludes.length > 0 && (
<div>
<h3 className="font-semibold">Precludes</h3>
<p>{course.precludes.join(', ')}</p>
</div>
)}

{course.prereqRaw && (
<div>
<h3 className="font-semibold">Prerequisite(s)</h3>
<p>{course.prereqRaw}</p>
</div>
)}
</div>
</aside>
);
}
Loading