Skip to content

Commit 9c34fb1

Browse files
authored
fix(web): onboarding program select search (#102)
1 parent 77e8187 commit 9c34fb1

File tree

3 files changed

+266
-314
lines changed

3 files changed

+266
-314
lines changed

apps/web/src/app/onboarding/component/onboarding-form.tsx

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { useRouter } from "next/navigation";
1414
import React, { Activity } from "react";
1515
import { toast } from "sonner";
1616
import z from "zod";
17-
import MultipleSelector from "@/app/onboarding/component/multiselect";
1817
import { SchoolCombobox } from "@/app/onboarding/component/school-combobox";
1918
import { Badge } from "@/components/ui/badge";
2019
import { Button } from "@/components/ui/button";
@@ -33,6 +32,7 @@ import {
3332
FieldLabel,
3433
Field as UIField,
3534
} from "@/components/ui/field";
35+
import MultipleSelector from "@/components/ui/multiselect";
3636
import {
3737
Select,
3838
SelectContent,
@@ -45,6 +45,7 @@ import {
4545
TooltipContent,
4646
TooltipTrigger,
4747
} from "@/components/ui/tooltip";
48+
import { useDebounce } from "@/hooks/use-debounce";
4849
import DegreeProgreeUpload from "@/modules/report-parsing/components/degree-progress-upload";
4950
import type { UserCourse } from "@/modules/report-parsing/types";
5051
import type { StartingTerm } from "@/modules/report-parsing/utils/parse-starting-term";
@@ -108,42 +109,41 @@ export function OnboardingForm() {
108109
);
109110

110111
// Programs
111-
const [programsQuery, setProgramsQuery] = React.useState<string | undefined>(
112-
undefined,
113-
);
114-
const {
115-
results: programs,
116-
status: programsStatus,
117-
loadMore: programsLoadMore,
118-
} = usePaginatedQuery(
112+
const [programSearchInput, setProgramSearchInput] =
113+
React.useState<string>("");
114+
const debouncedProgramSearch = useDebounce(programSearchInput, 300);
115+
116+
const { results: programs } = usePaginatedQuery(
119117
api.programs.getPrograms,
120-
isAuthenticated ? { query: programsQuery } : ("skip" as const),
118+
isAuthenticated
119+
? { query: debouncedProgramSearch.trim() || undefined }
120+
: ("skip" as const),
121121
{ initialNumItems: 20 },
122122
);
123123

124124
const programOptions = React.useMemo(
125125
() =>
126126
(programs ?? []).map((program) => ({
127127
value: program._id,
128-
label: program.name,
128+
label: `${program.name} - ${program.school}`,
129129
})),
130130
[programs],
131131
);
132132

133-
const handleSearchPrograms = React.useCallback(
134-
async (value: string) => {
135-
const trimmed = value.trim();
136-
setProgramsQuery(trimmed.length === 0 ? undefined : trimmed);
137-
return programOptions;
138-
},
139-
[programOptions],
133+
// Cache to store program labels so they don't disappear when search results change
134+
const programLabelCache = React.useRef<Map<Id<"programs">, string>>(
135+
new Map(),
140136
);
141137

142-
const handleLoadMorePrograms = React.useCallback(() => {
143-
if (programsStatus === "CanLoadMore") {
144-
void programsLoadMore(10);
145-
}
146-
}, [programsStatus, programsLoadMore]);
138+
// Update cache whenever new programs are loaded
139+
React.useEffect(() => {
140+
programOptions.forEach((option) => {
141+
programLabelCache.current.set(
142+
option.value as Id<"programs">,
143+
option.label,
144+
);
145+
});
146+
}, [programOptions]);
147147

148148
const currentYear = React.useMemo(() => new Date().getFullYear(), []);
149149
const defaultTerm = React.useMemo<Term>(() => {
@@ -372,8 +372,10 @@ export function OnboardingForm() {
372372
{(field) => {
373373
const selected = (field.state.value ?? []).map((p) => ({
374374
value: p,
375-
label: programOptions.find((val) => val.value === p)
376-
?.label as string,
375+
label:
376+
programOptions.find((val) => val.value === p)?.label ||
377+
programLabelCache.current.get(p) ||
378+
"",
377379
}));
378380
return (
379381
<UIField>
@@ -390,12 +392,16 @@ export function OnboardingForm() {
390392
}
391393
defaultOptions={programOptions}
392394
options={programOptions}
393-
delay={300}
394-
onSearch={handleSearchPrograms}
395-
triggerSearchOnFocus
396395
placeholder="Select your programs"
397-
commandProps={{ label: "Select programs" }}
398-
onListReachEnd={handleLoadMorePrograms}
396+
commandProps={{
397+
label: "Select programs",
398+
shouldFilter: false,
399+
}}
400+
inputProps={{
401+
onValueChange: (value) => {
402+
setProgramSearchInput(value);
403+
},
404+
}}
399405
emptyIndicator={
400406
<p className="text-center text-sm">
401407
No programs found

0 commit comments

Comments
 (0)