Skip to content

Henry/gradtrack styling fixes#1089

Open
Henrp wants to merge 2 commits intomainfrom
henry/gradtrack-styling-fixes
Open

Henry/gradtrack styling fixes#1089
Henrp wants to merge 2 commits intomainfrom
henry/gradtrack-styling-fixes

Conversation

@Henrp
Copy link
Contributor

@Henrp Henrp commented Mar 2, 2026

Summary

  • Fix drag-and-drop ghost placeholder appearing at dragged item's original position in GradTrak
  • Fix ratings panel loading flicker when navigating between cached courses
  • Fix Apollo cache merge warnings for Course.instructorAggregatedRatings and Course.classes
  • Change default semester status from "None" to "Incomplete" on plan creation
  • GradTrak layout tweaks: remove dashboard max-width, switch semester block to 90% width, reduce placeholder height, clean up button colors

Test plan

  • Drag a course within a SemesterBlock — verify no ghost placeholder at original position
  • Navigate between courses in ratings panel — verify no loading spinner flash on cached data
  • Create a new plan — verify semesters initialize with "Incomplete" status

@Henrp Henrp requested review from ARtheboss and PineND March 2, 2026 01:06
Comment on lines 62 to 241
ratingsCount?: number | null;
aggregatedRatings?: {
metrics: Array<{ metricName: string; weightedAverage: number }>;
} | null;
};

type EnrollmentSnapshot = Pick<
IEnrollmentSingular,
"enrolledCount" | "maxEnroll" | "endTime" | "activeReservedMaxCount"
>;

type ClassCardClass = Partial<BaseClassFields> & {
year?: number;
semester?: Semester;
course?: Partial<CourseSummary> | null;
primarySection?: {
enrollment?: {
latest?: Partial<EnrollmentSnapshot> | null;
} | null;
} | null;
};

interface ClassProps {
class?: ClassCardClass;
expandable?: boolean;
expanded?: boolean;
onExpandedChange?: (expanded: boolean) => void;
onDelete?: () => void;
leftBorderColor?: Color;
bookmarked?: boolean;
bookmarkToggle?: () => void;
active?: boolean;
wrapDescription?: boolean;
customActionMenu?: ReactNode;
onUnlock?: () => void;
}

export default function ClassCard({
class: _class,
expandable = false,
expanded,
onExpandedChange,
onDelete,
leftBorderColor = undefined,
bookmarked = false,
children,
active = false,
wrapDescription = false,
customActionMenu,
onUnlock = undefined,
...props
}: ClassProps & Omit<ComponentPropsWithRef<"div">, keyof ClassProps>) {
// bookmarked is part of the interface but not used in this component
void bookmarked;
const gradeDistribution =
_class?.course?.gradeDistribution ?? _class?.gradeDistribution;

const activeReservedMaxCount =
_class?.primarySection?.enrollment?.latest?.activeReservedMaxCount ?? 0;
const maxEnroll = _class?.primarySection?.enrollment?.latest?.maxEnroll ?? 0;

return (
<Card.RootColumn
style={{ overflow: "visible", position: "relative", ...props?.style }}
active={active}
{...props}
>
{leftBorderColor && (
<Card.LeftBorder
color={leftBorderColor}
style={{
position: "absolute",
left: 0,
top: 0,
bottom: 0,
height: "100%",
backgroundColor: `var(--${leftBorderColor}-500)`,
}}
/>
)}
<Card.ColumnHeader
style={{
overflow: "visible",
marginLeft: leftBorderColor ? "8px" : undefined,
}}
>
<Card.Body>
<div className={styles.cardContent}>
<div className={styles.topRow}>
<div className={styles.titleDescription}>
<Card.Heading>
{_class?.subject} {_class?.courseNumber}{" "}
<span className={styles.sectionNumber}>
#{formatClassNumber(_class?.number)}
<Card.Heading>
{_class?.subject} {_class?.courseNumber}{" "}
<span className={styles.sectionNumber}>
#{formatClassNumber(_class?.number)}
</span>
</Card.Heading>
<Card.Description wrapDescription={wrapDescription}>
{_class?.title ?? _class?.course?.title}
</Card.Description>
{_class?.semester && _class?.year && (
<span className={styles.semester}>
{formatSemester(_class.semester)} {_class.year}
</span>
)}
<Card.Footer>
<EnrollmentDisplay
enrolledCount={
_class?.primarySection?.enrollment?.latest?.enrolledCount
}
maxEnroll={_class?.primarySection?.enrollment?.latest?.maxEnroll}
time={_class?.primarySection?.enrollment?.latest?.endTime}
/>
{_class?.unitsMin !== undefined &&
_class.unitsMax !== undefined && (
<Units unitsMin={_class.unitsMin} unitsMax={_class.unitsMax} />
)}
{(_class?.primarySection?.enrollment?.latest
?.activeReservedMaxCount ?? 0) > 0 && (
<Tooltip
trigger={
<span className={styles.reservedSeating}>
<InfoCircle className={styles.reservedSeatingIcon} />
Rsvd
</span>
</Card.Heading>
<Card.Description wrapDescription={wrapDescription}>
{_class?.title ?? _class?.course?.title}
</Card.Description>
{_class?.semester && _class?.year && (
<span className={styles.semester}>
{formatSemester(_class.semester)} {_class.year}
}
title="Reserved Seating"
description={`${activeReservedMaxCount.toLocaleString()} out of ${maxEnroll.toLocaleString()} seats for this class are reserved.`}
/>
)}
{(_class?.course?.ratingsCount ?? 0) > 0 && (
<Tooltip
trigger={
<span className={styles.ratingsCount}>
<Star className={styles.ratingsIcon} />
{_class?.course?.ratingsCount}
</span>
)}
</div>
{gradeDistribution && (
<div className={styles.gradeContainer}>
<AverageGrade
gradeDistribution={gradeDistribution}
}
title="Ratings"
description={
<div
style={{
marginTop: 0.5,
fontSize: 14,
whiteSpace: "nowrap",
flexShrink: 0,
textAlign: "right",
display: "grid",
gridTemplateColumns: "auto auto auto",
gap: "4px 12px",
alignItems: "center",
}}
/>
</div>
)}
</div>
<Card.Footer className={styles.infoRow}>
<EnrollmentDisplay
enrolledCount={
_class?.primarySection?.enrollment?.latest?.enrolledCount
}
maxEnroll={
_class?.primarySection?.enrollment?.latest?.maxEnroll
>
{METRIC_ORDER.map((metricName) => {
const metric =
_class?.course?.aggregatedRatings?.metrics?.find(
(m) => m.metricName === metricName
);
if (!metric) return null;
const status = getMetricStatus(
metricName,
metric.weightedAverage
);
const color = getStatusColor(
metricName,
metric.weightedAverage
);
return [
<span key={`${metricName}-name`}>{metricName}:</span>,
<span
key={`${metricName}-status`}
style={{
color: `var(--${color}-500)`,
whiteSpace: "nowrap",
}}
>
{status}
</span>,
<span
key={`${metricName}-avg`}
style={{ color: "var(--secondary-text-color)" }}
>
{metric.weightedAverage.toFixed(1)}/5.0
</span>,
];
})}
</div>
}
time={_class?.primarySection?.enrollment?.latest?.endTime}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are unrelated changes? From your other PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops

Copy link
Contributor

@ARtheboss ARtheboss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some overlapping changes unrelated to Gradtrak. Gradtrak-related ones look good.

@Henrp Henrp force-pushed the henry/gradtrack-styling-fixes branch from 983a3c0 to 5aeea05 Compare March 4, 2026 21:27
@Henrp Henrp requested a review from ARtheboss March 4, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants