diff --git a/web/src/lib/components/CourseTable.svelte b/web/src/lib/components/CourseTable.svelte
index 4f6571f..c444fcc 100644
--- a/web/src/lib/components/CourseTable.svelte
+++ b/web/src/lib/components/CourseTable.svelte
@@ -14,6 +14,7 @@ import {
formatTimeRange,
getDeliveryConcern,
getPrimaryInstructor,
+ isAsyncOnline,
isMeetingTimeTBA,
isTimeTBA,
openSeats,
@@ -611,7 +612,12 @@ const table = createSvelteTable({
)}
passthrough
>
- {#if timeIsTBA(course)}
+ {#if isAsyncOnline(course)}
+ Async
+ {:else if timeIsTBA(course)}
TBA {
expect(result).toContain("\n\n");
});
});
+
+describe("isAsyncOnline", () => {
+ it("returns true for INT building with no times", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "INT",
+ building_description: "Internet Class",
+ begin_time: null,
+ end_time: null,
+ }),
+ ],
+ } as CourseResponse;
+ expect(isAsyncOnline(course)).toBe(true);
+ });
+ it("returns false for INT building with meeting times", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "INT",
+ building_description: "Internet Class",
+ tuesday: true,
+ thursday: true,
+ begin_time: "1000",
+ end_time: "1115",
+ }),
+ ],
+ } as CourseResponse;
+ expect(isAsyncOnline(course)).toBe(false);
+ });
+ it("returns false for non-INT building", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "MH",
+ building_description: "Main Hall",
+ begin_time: null,
+ end_time: null,
+ }),
+ ],
+ } as CourseResponse;
+ expect(isAsyncOnline(course)).toBe(false);
+ });
+ it("returns false for empty meeting times", () => {
+ const course = { meetingTimes: [] } as unknown as CourseResponse;
+ expect(isAsyncOnline(course)).toBe(false);
+ });
+});
+
+describe("formatLocationDisplay", () => {
+ it("returns 'Online' for INT building", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "INT",
+ building_description: "Internet Class",
+ }),
+ ],
+ campus: "9",
+ } as CourseResponse;
+ expect(formatLocationDisplay(course)).toBe("Online");
+ });
+ it("returns building and room for physical location", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "MH",
+ building_description: "Main Hall",
+ room: "2.206",
+ }),
+ ],
+ campus: "11",
+ } as CourseResponse;
+ expect(formatLocationDisplay(course)).toBe("MH 2.206");
+ });
+ it("returns building only when no room", () => {
+ const course = {
+ meetingTimes: [
+ makeMeetingTime({
+ building: "MH",
+ building_description: "Main Hall",
+ room: null,
+ }),
+ ],
+ campus: "11",
+ } as CourseResponse;
+ expect(formatLocationDisplay(course)).toBe("MH");
+ });
+});
diff --git a/web/src/lib/course.ts b/web/src/lib/course.ts
index a4397de..de966ca 100644
--- a/web/src/lib/course.ts
+++ b/web/src/lib/course.ts
@@ -152,6 +152,13 @@ export function isTimeTBA(mt: DbMeetingTime): boolean {
return !mt.begin_time || mt.begin_time.length !== 4;
}
+/** Check if course is asynchronous online (INT building with no meeting times) */
+export function isAsyncOnline(course: CourseResponse): boolean {
+ if (course.meetingTimes.length === 0) return false;
+ const mt = course.meetingTimes[0];
+ return mt.building === "INT" && isTimeTBA(mt);
+}
+
/** Format a date string to "January 20, 2026". Accepts YYYY-MM-DD or MM/DD/YYYY. */
export function formatDate(dateStr: string): string {
let year: number, month: number, day: number;
@@ -170,6 +177,8 @@ export function formatDate(dateStr: string): string {
/** Short location string from first meeting time: "MH 2.206" or campus fallback */
export function formatLocation(course: CourseResponse): string | null {
for (const mt of course.meetingTimes) {
+ // Skip INT building - handled by formatLocationDisplay
+ if (mt.building === "INT") continue;
if (mt.building && mt.room) return `${mt.building} ${mt.room}`;
if (mt.building) return mt.building;
}
@@ -307,13 +316,19 @@ export function concernAccentColor(concern: DeliveryConcern): string | null {
/**
* Location display text for the table cell.
- * Falls back to "Online" for online courses instead of showing a dash.
+ * Shows "Online" for internet class (INT building) or other online courses.
*/
export function formatLocationDisplay(course: CourseResponse): string | null {
+ // Check for Internet Class building first
+ const hasIntBuilding = course.meetingTimes.some((mt) => mt.building === "INT");
+ if (hasIntBuilding) return "Online";
+
const loc = formatLocation(course);
if (loc) return loc;
+
const concern = getDeliveryConcern(course);
- if (concern === "online") return "Online";
+ if (concern === "online" || concern === "internet") return "Online";
+
return null;
}