Skip to content

Commit 1ed798f

Browse files
authored
feat: launch PlanNEU (#352)
2 parents 033f746 + 0a02bb7 commit 1ed798f

16 files changed

Lines changed: 120 additions & 116 deletions

File tree

apps/cli/src/tools/sync-db.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,12 @@ export default defineCommand({
275275
`buildings: ${prunable.buildings.map((e) => e.code).join(", ")}`,
276276
);
277277
if (!args.dryRun) {
278-
await db
279-
.delete(buildingsT)
280-
.where(
281-
inArray(
282-
buildingsT.id,
283-
prunable.buildings.map((e) => e.id),
284-
),
285-
);
278+
await db.delete(buildingsT).where(
279+
inArray(
280+
buildingsT.id,
281+
prunable.buildings.map((e) => e.id),
282+
),
283+
);
286284
}
287285
}
288286

@@ -291,14 +289,12 @@ export default defineCommand({
291289
`subjects: ${prunable.subjects.map((e) => e.code).join(", ")}`,
292290
);
293291
if (!args.dryRun) {
294-
await db
295-
.delete(subjectsT)
296-
.where(
297-
inArray(
298-
subjectsT.id,
299-
prunable.subjects.map((e) => e.id),
300-
),
301-
);
292+
await db.delete(subjectsT).where(
293+
inArray(
294+
subjectsT.id,
295+
prunable.subjects.map((e) => e.id),
296+
),
297+
);
302298
}
303299
}
304300

@@ -307,14 +303,12 @@ export default defineCommand({
307303
`campuses: ${prunable.campuses.map((e) => e.code).join(", ")}`,
308304
);
309305
if (!args.dryRun) {
310-
await db
311-
.delete(campusesT)
312-
.where(
313-
inArray(
314-
campusesT.id,
315-
prunable.campuses.map((e) => e.id),
316-
),
317-
);
306+
await db.delete(campusesT).where(
307+
inArray(
308+
campusesT.id,
309+
prunable.campuses.map((e) => e.id),
310+
),
311+
);
318312
}
319313
}
320314

@@ -361,9 +355,7 @@ function formatDriftLines(
361355
if (isVerbose()) {
362356
for (const e of entries) {
363357
const status =
364-
e.refCount > 0
365-
? pc.red(`${e.refCount} refs`)
366-
: pc.green("prunable");
358+
e.refCount > 0 ? pc.red(`${e.refCount} refs`) : pc.green("prunable");
367359
lines.push(` ${pc.dim(e.code)} (${status})`);
368360
}
369361
}

apps/searchneu/app/api/scheduler/saved-plans/[id]/route.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ export async function PATCH(
225225
...(body.startTime && { startTime: body.startTime }),
226226
...(body.endTime && { endTime: body.endTime }),
227227
...(body.freeDays && { freeDays: body.freeDays }),
228-
...(body.includeHonorsSections && {
228+
...(body.includeHonorsSections !== undefined && {
229229
includeHonorsSections: body.includeHonorsSections,
230230
}),
231-
...(body.includeRemoteSections && {
231+
...(body.includeRemoteSections !== undefined && {
232232
includeRemoteSections: body.includeRemoteSections,
233233
}),
234-
...(body.hideFilledSections && {
234+
...(body.hideFilledSections !== undefined && {
235235
hideFilledSections: body.hideFilledSections,
236236
}),
237237
...(body.campus && { campus: body.campus }),

apps/searchneu/app/scheduler/generator/page.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { getTerms } from "@/lib/dal/terms";
33
import { getCampuses } from "@/lib/dal/campuses";
44
import { getNupaths } from "@/lib/dal/nupaths";
55

6-
import { db, nupathsT, savedPlansT, savedPlanCoursesT } from "@/lib/db";
6+
import { db, nupathsT, savedPlansT } from "@/lib/db";
77
import { auth } from "@/lib/auth/auth";
88
import { headers } from "next/headers";
9-
import { redirect } from "next/navigation";
9+
import { notFound, redirect } from "next/navigation";
1010
import { eq, and } from "drizzle-orm";
1111

1212
export default async function Page({
@@ -40,12 +40,13 @@ export default async function Page({
4040
eq(savedPlansT.userId, session.user.id),
4141
),
4242
});
43-
if (!plan) {
44-
return <div>Plan not found</div>;
45-
}
4643
} catch (error) {
4744
console.error("Error loading plan:", error);
48-
return <div>Error loading plan</div>;
45+
return notFound();
46+
}
47+
48+
if (!plan) {
49+
return notFound();
4950
}
5051

5152
// Fetch available NUPath options
Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,10 @@
11
import { Header } from "@/components/navigation/Header";
2-
import { schedulerFlag } from "@/lib/flags";
3-
import { notFound } from "next/navigation";
4-
import { Suspense, type ReactNode } from "react";
52

63
export default async function Layout({ children }: LayoutProps<"/rooms">) {
74
return (
85
<div className="flex h-screen flex-col overflow-hidden pt-4">
96
<Header />
10-
<div className="min-h-0 flex-1">
11-
<Suspense>
12-
<FlagCheck>{children}</FlagCheck>
13-
</Suspense>
14-
</div>
7+
<div className="min-h-0 flex-1">{children}</div>
158
</div>
169
);
1710
}
18-
19-
async function FlagCheck({ children }: { children: ReactNode }) {
20-
const schedulerPage = await schedulerFlag();
21-
22-
if (!schedulerPage) {
23-
notFound();
24-
}
25-
26-
return children;
27-
}

apps/searchneu/components/ClientLayout.tsx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
"use client";
22

3-
import { useEffect } from "react";
4-
import { toast, Toaster } from "sonner";
3+
import { Toaster } from "sonner";
54
import FeedbackModal from "@/components/feedback/FeedbackModal";
6-
import { X } from "lucide-react";
7-
import { SadHusky } from "./icons/SadHusky";
85

96
export function ClientLayout({ children }: { children: React.ReactNode }) {
10-
useEffect(() => {
11-
if (!sessionStorage.getItem("summer-delay-shown")) {
12-
toast("Summer course catalog delayed :(", {
13-
description: "We are working to get them to you soon.",
14-
position: "bottom-left",
15-
icon: <SadHusky />,
16-
cancel: {
17-
label: <X size={20} color="#F15B50" className="h-[16px] w-[16px]" />,
18-
onClick: () => {},
19-
},
20-
});
21-
sessionStorage.setItem("summer-delay-shown", "true");
22-
}
23-
}, []);
7+
// useEffect(() => {
8+
// if (!sessionStorage.getItem("summer-delay-shown")) {
9+
// toast("Summer course catalog delayed :(", {
10+
// description: "We are working to get them to you soon.",
11+
// position: "bottom-left",
12+
// icon: <SadHusky />,
13+
// cancel: {
14+
// label: <X size={20} color="#F15B50" className="h-[16px] w-[16px]" />,
15+
// onClick: () => {},
16+
// },
17+
// });
18+
// sessionStorage.setItem("summer-delay-shown", "true");
19+
// }
20+
// }, []);
2421

2522
return (
2623
<div>

apps/searchneu/components/navigation/Header.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import Link from "next/link";
22
import { Logo } from "../icons/logo";
33
import { UserIcon } from "./UserMenu";
4-
import { graduateFlag, roomsFlag, schedulerFlag } from "@/lib/flags";
4+
import { graduateFlag, roomsFlag } from "@/lib/flags";
55
import { Suspense } from "react";
66
import { NavBar } from "./NavBar";
77

88
export function Header() {
99
const enableRoomsPage = roomsFlag();
10-
const enableSchedulerPage = schedulerFlag();
1110
const enableGraduatePage = graduateFlag();
1211

1312
const flags = {
1413
rooms: enableRoomsPage,
15-
scheduler: enableSchedulerPage,
1614
graduate: enableGraduatePage,
1715
};
1816

apps/searchneu/components/navigation/NavBar.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export function NavBar({
2222
closeable?: boolean;
2323
}) {
2424
const roomsFlag = use(flags["rooms"]);
25-
const schedulerFlag = use(flags["scheduler"]);
2625
const graduateFlag = use(flags["graduate"]);
2726

2827
const pathname = usePathname();
@@ -32,7 +31,6 @@ export function NavBar({
3231
<FlagValues
3332
values={{
3433
rooms: roomsFlag,
35-
scheduler: schedulerFlag,
3634
}}
3735
/>
3836
{roomsFlag && (
@@ -69,11 +67,9 @@ export function NavBar({
6967
<span>Catalog</span>
7068
</Link>
7169
</LinkWrapper>
72-
{schedulerFlag && (
73-
<LinkWrapper mobileNav={closeable}>
74-
<SchedulerButton pathname={pathname} />
75-
</LinkWrapper>
76-
)}
70+
<LinkWrapper mobileNav={closeable}>
71+
<SchedulerButton pathname={pathname} />
72+
</LinkWrapper>
7773
<LinkWrapper mobileNav={closeable}>
7874
<Link
7975
href="/notifications"

apps/searchneu/components/scheduler/generator/SchedulerWrapper.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ import { SchedulerView } from "./calendar/SchedulerView";
1313
import { ScheduleSidebar } from "./right-sidebar/ScheduleSidebar";
1414
import { FilterPanel } from "./left-sidebar/FilterPanel";
1515
import { GroupedTerms, Campus, Nupath } from "@/lib/catalog/types";
16+
import {
17+
PlanData,
18+
PlanCourse,
19+
PlanSection,
20+
PlanUpdateData,
21+
} from "@/lib/scheduler/types";
1622

1723
interface SchedulerWrapperProps {
1824
nupathOptions: { label: string; value: string }[];
@@ -128,7 +134,7 @@ export function SchedulerWrapper({
128134
return;
129135
}
130136

131-
const planData = await response.json();
137+
const planData = (await response.json()) as PlanData;
132138
setPlanId(planIdNum);
133139
setPlanName(planData.name);
134140

@@ -138,13 +144,13 @@ export function SchedulerWrapper({
138144
const allCourseIds: number[] = [];
139145

140146
if (planData.courses && Array.isArray(planData.courses)) {
141-
planData.courses.forEach((course: any) => {
147+
planData.courses.forEach((course: PlanCourse) => {
142148
allCourseIds.push(course.courseId);
143149
if (course.isLocked) {
144150
lockedCourseIds.add(course.courseId);
145151
}
146152
if (course.sections && Array.isArray(course.sections)) {
147-
course.sections.forEach((section: any) => {
153+
course.sections.forEach((section: PlanSection) => {
148154
if (section.isHidden) {
149155
hiddenSectionIds.add(section.sectionId);
150156
}
@@ -158,7 +164,7 @@ export function SchedulerWrapper({
158164
...filters,
159165
startTime: planData.startTime,
160166
endTime: planData.endTime,
161-
specificDaysFree: planData.freeDays,
167+
specificDaysFree: planData.freeDays?.map(Number),
162168
includeHonors: planData.includeHonorsSections,
163169
includesRemote: planData.includeRemoteSections,
164170
minSeatsLeft: planData.hideFilledSections ? 1 : undefined,
@@ -326,14 +332,14 @@ export function SchedulerWrapper({
326332
const lockedChanged = !setsEqual(currentLocked, prevLocked);
327333
const hiddenChanged = !setsEqual(currentHidden, prevHidden);
328334

329-
const updateData: any = {
335+
const updateData: PlanUpdateData = {
330336
startTime: filters.startTime ?? null,
331337
endTime: filters.endTime ?? null,
332338
freeDays: filters.specificDaysFree ?? [],
333339
includeHonorsSections: filters.includeHonors ?? false,
334340
includeRemoteSections: filters.includesRemote ?? true,
335341
hideFilledSections: (filters.minSeatsLeft ?? 0) > 0,
336-
nupaths: filters.nupaths ?? [],
342+
nupaths: [],
337343
numCourses: filters.numCourses,
338344
};
339345

apps/searchneu/components/scheduler/shared/modal/AddCoursesModal.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,9 @@ export default function AddCoursesModal(props: AddCoursesModalProps) {
139139
}, [props.planId, props.open]);
140140

141141
const activeTerm = props.selectedTerm ?? props.terms.neu[0];
142-
const activeTermLabel =
143-
Object.values(props.terms)
144-
.flat()
145-
.find((t) => t.term === activeTerm)?.name ?? "Selected Term";
142+
const activeTermLabel = Object.values(props.terms)
143+
.flat()
144+
.find((t) => t.term === activeTerm.term)?.name;
146145

147146
// helpers
148147

@@ -314,7 +313,7 @@ export default function AddCoursesModal(props: AddCoursesModalProps) {
314313
"Content-Type": "application/json",
315314
},
316315
body: JSON.stringify({
317-
term: activeTerm.id,
316+
term: activeTerm.term + activeTerm.part,
318317
courses,
319318
numCourses: numCoursesValue,
320319
}),

apps/searchneu/components/scheduler/shared/modal/SelectedCourseGroup.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const SelectedCourseGroup = ({
5353
{/* parent */}
5454
<SelectedCourseItem
5555
course={{
56-
subject: (parent as any).subjectCode || parent.subject,
56+
subject: parent.subject as unknown as string, // TODO: fix casting after correctly typing SearchResult and Course from modal
5757
courseNumber: parent.courseNumber,
5858
title: parent.name,
5959
handleDelete: () => onDeleteCourse(parent, false),
@@ -66,7 +66,7 @@ const SelectedCourseGroup = ({
6666
<div key={idx} className="border-neu3 border-t">
6767
<SelectedCourseItem
6868
course={{
69-
subject: (coreq as any).subjectCode || (coreq as any).subject,
69+
subject: coreq.subject as unknown as string, // TODO: fix casting after correctly typing SearchResult and Course from modal
7070
courseNumber: coreq.courseNumber,
7171
title: coreq.name ?? "Corequisite",
7272
handleDelete: () => onDeleteCourse(coreq, true),

0 commit comments

Comments
 (0)