Skip to content

feat: roadmap search shortcut pills (#1815)#1907

Open
KevinWu098 wants to merge 18 commits into
mainfrom
kwu/roadmap-shortcut-pills-7722
Open

feat: roadmap search shortcut pills (#1815)#1907
KevinWu098 wants to merge 18 commits into
mainfrom
kwu/roadmap-shortcut-pills-7722

Conversation

@KevinWu098

@KevinWu098 KevinWu098 commented Jun 7, 2026

Copy link
Copy Markdown
Member

Summary

Migrate "Search with Roadmap" from a dedicated autocomplete input to shortcut pills below the quick search input.

Chunk 1 — Schema & Backend:

  • Add courseId field to WebsocSearchInputSchema, matching the Anteater API's existing support for querying WebSOC by course ID directly
  • Extend getManyOfField's fieldName enum to accept 'courseId' in addition to 'ge', enabling unified fan-out queries

Chunk 2 — URL State & Query Logic:

  • Add courseIds as a nuqs array param inside courseSearchParamParsers as an advanced search param (not UI-exposed, like excludeRoadmapCourses)
  • Update CourseRenderPane query logic: formData.courseIds.length > 0 takes priority → getManyOfField with fieldName: 'courseId'unionWebsocResponses
  • Delete RightPaneStore.ts entirely — multiSearchData is fully replaced by the URL param
  • Update isValidSearch and shouldShowSearchForm to accept courseIds
  • Simplify queryKeys — courseIds included in formData serialization automatically

Chunk 4 — RoadmapPill Component:

  • New RoadmapPill split-button: left = search with active roadmap's courses, right = dropdown to pick a different roadmap
  • Only renders when signed in and roadmaps with courses for current term exist
  • Animates in via MUI Grow transition
  • Dropdown shows all roadmaps with disabled state + reason for roadmaps without courses for the term

Chunk 5 — Layout:

  • Replace SearchWithPlanner autocomplete + "or" divider in QuickSearch with pill row below FuzzySearch
  • Delete SearchWithPlanner.tsx (242 lines removed)
  • Stub slot in pill row for future pills (department, recent search)

Chunk 6 — Cleanup:

  • Remove COURSE_SEARCH_PLANNER_KEY, plannerSearchParser, shouldSearchPlannerFromParams()
  • Simplify useCourseSearchMode — no longer gated on planner param
  • ScheduleManagement tab detection uses hasAdvancedParams (covers courseIds)
  • Restore "not offered" warnings for courseIds searches (diff requested vs returned)
  • Remove filterTakenCourses WarningAlert from CourseRenderPane

Test Plan

  • tsc --noEmit passes (only pre-existing errors from missing generated data files)
  • oxlint --deny-warnings passes (0 warnings, 0 errors)
  • vitest run — all 34 tests pass; 4 test suites fail due to pre-existing missing termData.json

Issues

Closes #1815

Open in Web Open in Cursor 

- Add courseId field to WebsocSearchInputSchema, matching the Anteater
  API's existing support for querying WebSOC by course ID
- Extend getManyOfField's fieldName enum to accept 'courseId' in
  addition to 'ge', enabling fan-out queries for multiple course IDs

Part of #1815
- Add courseIds nuqs array parser and useCourseIds hook for URL-driven
  planner course search state
- Update CourseRenderPane to branch on courseIds: fan out via
  getManyOfField with fieldName 'courseId', then unionWebsocResponses
- Update SearchWithPlanner to write courseIds directly to URL instead
  of resolving via trpc.course.getMultiple + RightPaneStore
- Update PlannerCourseLinkBanner to read courseIds from URL
- Remove useQuickSearch's RightPaneStore dependency
- Delete RightPaneStore.ts (multiSearchData was its only purpose)
- Simplify queryKeys to use courseIds array instead of serialized
  multiSearchData

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 06:44 Inactive
- courseIds is now part of courseSearchParamParsers, making it part of
  formData alongside ge, deptValue, etc.
- Remove standalone courseIdsParser, useCourseIds hook, COURSE_IDS_KEY
- Update isValidSearch and shouldShowSearchForm to accept courseIds
- CourseRenderPane reads formData.courseIds directly
- queryKeys simplified — courseIds included in formData serialization
- Add courseIds default to DEFAULT_FORM_DATA

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 07:17 Inactive
Move courseIds default into DEFAULT_ADVANCED_SEARCH_VALUES and add it
to ADVANCED_SEARCH_PARAMS and advancedSearchParsers. This categorizes
it alongside other non-UI-exposed params like excludeRoadmapCourses.

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 08:01 Inactive
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 08:05 Inactive
Split-button pill for quick roadmap search:
- Left side: click to search with the active roadmap's courses
- Right side: dropdown to pick a different roadmap
- Only renders when signed in and roadmaps with courses for the
  current term are available
- Animates in via MUI Grow transition
- Dropdown shows all roadmaps, disabled ones labeled with reason

Part of #1815
- Remove 'or' text + SearchWithPlanner autocomplete from QuickSearch
- Add pill row below FuzzySearch with RoadmapPill as first pill
- Delete SearchWithPlanner.tsx entirely
- Leave commented slot in pill row for future pills (department, etc.)

Part of #1815
- Remove COURSE_SEARCH_PLANNER_KEY constant
- Remove plannerSearchParser export
- Remove shouldSearchPlannerFromParams() from plannerHelpers
- Remove plannerSearchParam from useCourseSearchMode (manual mode
  no longer gated on planner param absence)
- Update ScheduleManagement to rely on hasAdvancedParams (which now
  covers courseIds) instead of shouldSearchPlannerFromParams

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 08:30 Inactive
@KevinWu098 KevinWu098 marked this pull request as ready for review June 7, 2026 17:00
- Bring back 'X is not offered' warnings for courseIds searches by
  diffing requested courseIds against courseIds returned by WebSOC
- Remove the filterTakenCourses WarningAlert
- Remove usePlannerStore import from CourseRenderPane

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 17:06 Inactive

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx:93">
P2: `unofferedCourseIds` is mutated inside async `queryFn`, which can leave stale/mismatched warning alerts after failed or out-of-order requests.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

);

const returnedIds = new Set(flattenCourses(response).map((c) => c.courseId));
setUnofferedCourseIds(formData.courseIds.filter((id) => !returnedIds.has(id)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: unofferedCourseIds is mutated inside async queryFn, which can leave stale/mismatched warning alerts after failed or out-of-order requests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx, line 93:

<comment>`unofferedCourseIds` is mutated inside async `queryFn`, which can leave stale/mismatched warning alerts after failed or out-of-order requests.</comment>

<file context>
@@ -89,7 +88,11 @@ export function CourseRenderPane({ onDismissSearchResults }: CourseRenderPanePro
                     );
+
+                    const returnedIds = new Set(flattenCourses(response).map((c) => c.courseId));
+                    setUnofferedCourseIds(formData.courseIds.filter((id) => !returnedIds.has(id)));
                 } else {
+                    setUnofferedCourseIds([]);
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx:117">
P2: Roadmaps are enabled by term presence instead of searchable (non-custom) course IDs, so custom-only roadmaps appear selectable but always fail at search time.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 17:58 Inactive
@KevinWu098

Copy link
Copy Markdown
Member Author

/deploy-shared

TODO: Remove before merging. Bypasses auth check and injects fake
roadmaps (CS Major + GE) covering 2024-2026 so the pill renders
without a real planner session.
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 22:34 Inactive

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx:93">
P2: `unofferedCourseIds` is mutated inside async `queryFn`, which can leave stale/mismatched warning alerts after failed or out-of-order requests.</violation>
</file>

<file name="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx:17">
P3: New TODO comment is anonymous; add an owner handle to match repository TODO conventions.

(Based on your team's feedback about naming TODO owners.) [FEEDBACK_USED].</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

import { useCallback, useMemo, useRef, useState } from 'react';
import { useShallow } from 'zustand/react/shallow';

// TODO: Remove mock data before merging. Hardcoded roadmaps for testing the pill UI.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: New TODO comment is anonymous; add an owner handle to match repository TODO conventions.

(Based on your team's feedback about naming TODO owners.) .

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx, line 17:

<comment>New TODO comment is anonymous; add an owner handle to match repository TODO conventions.

(Based on your team's feedback about naming TODO owners.) .</comment>

<file context>
@@ -14,6 +14,34 @@ import type { AATerm, Roadmap } from '@packages/antalmanac-types';
 import { useCallback, useMemo, useRef, useState } from 'react';
 import { useShallow } from 'zustand/react/shallow';
 
+// TODO: Remove mock data before merging. Hardcoded roadmaps for testing the pill UI.
+const MOCK_ROADMAPS: Roadmap[] = [
+    {
</file context>

- Full pill-radius (9999) on ButtonGroup with inherited radii on children
- Menu anchored bottom-left with small gap, rounded paper, subtle shadow
- Menu items have slight rounding and horizontal margin

Part of #1815
@cursor cursor Bot temporarily deployed to staging-1907 June 7, 2026 22:54 Inactive
…pills-7722

# Conflicts:
#	apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/SearchWithPlanner.tsx
#	apps/antalmanac/src/lib/plannerHelpers.ts
@cursor cursor Bot temporarily deployed to staging-1907 June 8, 2026 01:58 Inactive

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/CourseRenderPane/CourseRenderPane.tsx:93">
P2: `unofferedCourseIds` is mutated inside async `queryFn`, which can leave stale/mismatched warning alerts after failed or out-of-order requests.</violation>
</file>

<file name="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx">

<violation number="1" location="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx:17">
P3: New TODO comment is anonymous; add an owner handle to match repository TODO conventions.

(Based on your team's feedback about naming TODO owners.) [FEEDBACK_USED].</violation>

<violation number="2" location="apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx:383">
P2: Dropdown visibility is tied to searchable roadmaps only, which hides the roadmap menu when there is one valid roadmap plus additional non-searchable roadmaps.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic


// TODO: Restore sessionIsValid-only gate before merging (drop usingMockRoadmaps bypass).
const showPill = isSignedIn && !isPlannerLoading && roadmapsForTerm.length > 0;
const showMenu = roadmapsForTerm.length > 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Dropdown visibility is tied to searchable roadmaps only, which hides the roadmap menu when there is one valid roadmap plus additional non-searchable roadmaps.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/antalmanac/src/components/RightPane/CoursePane/SearchForm/QuickSearch/RoadmapPill.tsx, line 383:

<comment>Dropdown visibility is tied to searchable roadmaps only, which hides the roadmap menu when there is one valid roadmap plus additional non-searchable roadmaps.</comment>

<file context>
@@ -1,211 +1,486 @@
+
+        // TODO: Restore sessionIsValid-only gate before merging (drop usingMockRoadmaps bypass).
+        const showPill = isSignedIn && !isPlannerLoading && roadmapsForTerm.length > 0;
+        const showMenu = roadmapsForTerm.length > 1;
+
+        useEffect(() => {
</file context>
Suggested change
const showMenu = roadmapsForTerm.length > 1;
const showMenu = sortedRoadmaps.length > 1;

@Choollol

Choollol commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Looking pretty good so far

  1. There's not a lot of space for the actual roadmap name right now, maybe the "Planner" could part be moved into a tooltip or max width could be extended?
  2. Also probably a bug, clicking on an option in the dropdown menu insta searches without actually selecting it.
  3. Some tooltips might also be nice but it's not too important

@KevinWu098

Copy link
Copy Markdown
Member Author

Looking pretty good so far

  1. There's not a lot of space for the actual roadmap name right now, maybe the "Planner" could part be moved into a tooltip or max width could be extended?
  2. Also probably a bug, clicking on an option in the dropdown menu insta searches without actually selecting it.
  3. Some tooltips might also be nice but it's not too important
  1. mm open to extending / adjusting the styling here
  2. we don't store which planner people choose (in localStorage or otherwise) and generally expect planner roadmaps to == 1, so its intentional that its a one click submit. also open to playing with this, but i think the behavior is good.
  3. /shrug

@KevinWu098

Copy link
Copy Markdown
Member Author

/deploy

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.

Change roadmap search to shortcut buttons.

3 participants