From d51d76d32a301b9b59d0f717b1ad78188ab3044c Mon Sep 17 00:00:00 2001 From: Gslmao Date: Fri, 12 Jun 2026 20:17:57 +0530 Subject: [PATCH 1/5] feat: SelectField has type to search --- src/components/cards/FacultySelector.tsx | 101 ++++++++++++++++------- 1 file changed, 71 insertions(+), 30 deletions(-) diff --git a/src/components/cards/FacultySelector.tsx b/src/components/cards/FacultySelector.tsx index a952e3e..4efcf9b 100644 --- a/src/components/cards/FacultySelector.tsx +++ b/src/components/cards/FacultySelector.tsx @@ -43,37 +43,73 @@ type SelectFieldProps = { function SelectField({ label, value, options, onChange, renderOption }: SelectFieldProps) { const [isOpen, setIsOpen] = useState(false); + const [search, setSearch] = useState(''); const ref = useRef(null); + const inputRef = useRef(null); - // Close dropdown on outside click useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (ref.current && !ref.current.contains(event.target as Node)) { setIsOpen(false); + setSearch(''); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); + // Focus input when dropdown opens + useEffect(() => { + if (isOpen) inputRef.current?.focus(); + }, [isOpen]); + + const filteredOptions = options.filter(option => { + const label = renderOption ? renderOption(option) : option; + return label.toLowerCase().includes(search.toLowerCase()); + }); + const selectedLabel = value ? (renderOption ? renderOption(value) : value) : `Select ${label}`; + const handleToggle = () => { + setIsOpen(prev => { + if (prev) setSearch(''); + return !prev; + }); + }; + return (
-
- + + {isOpen && (
    - {options.map((option, index) => ( -
  • { - onChange(option); - setIsOpen(false); - }} - className={` - px-4 py-2 cursor-pointer hover:bg-[#FFEA79] - ${value === option ? 'bg-[#C1FF83] font-bold' : ''} - `} - > - {renderOption ? renderOption(option) : option} -
  • - ))} + {filteredOptions.length > 0 ? ( + filteredOptions.map((option, index) => ( +
  • { + onChange(option); + setIsOpen(false); + setSearch(''); + }} + className={` + px-4 py-2 cursor-pointer hover:bg-[#FFEA79] + ${value === option ? 'bg-[#C1FF83] font-bold' : ''} + `} + > + {renderOption ? renderOption(option) : option} +
  • + )) + ) : ( +
  • No results
  • + )}
)} @@ -708,7 +749,7 @@ export default function FacultySelector({ {getCourseType(selectedSubject.split(' - ')[0]) === 'P' && !selectedSubject.split(' - ')[0].startsWith('BSTS') ? ( { if (e === 'morning' || e === 'evening') { From 229c430f188f035950f303c201d1cc0fbddf6cd7 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Sat, 13 Jun 2026 00:24:04 +0530 Subject: [PATCH 2/5] sorry: minor fix (pls squash and merge) --- src/components/cards/FacultySelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/cards/FacultySelector.tsx b/src/components/cards/FacultySelector.tsx index 4efcf9b..7f63998 100644 --- a/src/components/cards/FacultySelector.tsx +++ b/src/components/cards/FacultySelector.tsx @@ -749,7 +749,7 @@ export default function FacultySelector({ {getCourseType(selectedSubject.split(' - ')[0]) === 'P' && !selectedSubject.split(' - ')[0].startsWith('BSTS') ? ( { if (e === 'morning' || e === 'evening') { From a6053cede057e2f77fb9d5ed3a19ad89f16a1671 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Mon, 22 Jun 2026 09:07:19 +0530 Subject: [PATCH 3/5] use: premade component for combobox --- src/components/cards/FacultySelector.tsx | 10 +++++----- src/components/ui/ComboBox.tsx | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/cards/FacultySelector.tsx b/src/components/cards/FacultySelector.tsx index 7f63998..acb7995 100644 --- a/src/components/cards/FacultySelector.tsx +++ b/src/components/cards/FacultySelector.tsx @@ -8,7 +8,7 @@ import { data } from '@/data/faculty'; import { fullCourseData } from '@/lib/type'; import AlertModal from '../ui/AlertModal'; import { course_type_map } from '@/lib/course_codes_map'; - +import ComboBox from '@/components/ui/ComboBox'; const schools = [ 'SCOPE', 'SCORE', @@ -733,14 +733,14 @@ export default function FacultySelector({
- - {getCourseType(selectedSubject.split(' - ')[0]) === 'P' && !selectedSubject.split(' - ')[0].startsWith('BSTS') ? ( - { @@ -763,7 +763,7 @@ export default function FacultySelector({ )} /> ) : ( - a.localeCompare(b))} diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx index ef4e88c..c65c5e7 100644 --- a/src/components/ui/ComboBox.tsx +++ b/src/components/ui/ComboBox.tsx @@ -7,9 +7,10 @@ type ComboBoxProps = { value: string; options: string[]; onChange: (val: string) => void; + renderOption?: (val: string) => string; }; -export default function ComboBox({ label, value, options, onChange }: ComboBoxProps) { +export default function ComboBox({ label, value, options, onChange, renderOption }: ComboBoxProps) { const [isOpen, setIsOpen] = useState(false); const [inputValue, setInputValue] = useState(''); const ref = useRef(null); @@ -24,18 +25,20 @@ export default function ComboBox({ label, value, options, onChange }: ComboBoxPr return () => document.removeEventListener('mousedown', handleClickOutside); }, []); - const filteredOptions = options.filter(option => - option.toLowerCase().includes(inputValue.toLowerCase()) - ); + const filteredOptions = options.filter(option => { + const label = renderOption ? renderOption(option) : option; + return label.toLowerCase().includes(inputValue.toLowerCase()); + }); const handleSelect = (option: string) => { + const renderedOption = renderOption ? renderOption(option) : option; onChange(option); - setInputValue(option); + setInputValue(renderedOption); setIsOpen(false); }; useEffect(() => { - if (value) setInputValue(value); + if (value) setInputValue(renderOption ? renderOption(value) : value); }, [value]); return ( @@ -82,7 +85,7 @@ export default function ComboBox({ label, value, options, onChange }: ComboBoxPr
{isOpen && ( -
    +
      {filteredOptions.map((option, index) => (
    • - {option} + {renderOption ? renderOption(option) : option}
    • ))}
    From a3f07155211319d505d9774b26c34c2a4a43d73f Mon Sep 17 00:00:00 2001 From: Gslmao Date: Mon, 22 Jun 2026 09:08:28 +0530 Subject: [PATCH 4/5] remove: unused selectField component --- src/components/cards/FacultySelector.tsx | 118 ----------------------- 1 file changed, 118 deletions(-) diff --git a/src/components/cards/FacultySelector.tsx b/src/components/cards/FacultySelector.tsx index acb7995..a48aeb6 100644 --- a/src/components/cards/FacultySelector.tsx +++ b/src/components/cards/FacultySelector.tsx @@ -33,124 +33,6 @@ const schools = [ // 'MTech (Fresher)', ]; -type SelectFieldProps = { - label: string; - value: string; - options: string[]; - onChange: (val: string) => void; - renderOption?: (option: string) => string; -}; - -function SelectField({ label, value, options, onChange, renderOption }: SelectFieldProps) { - const [isOpen, setIsOpen] = useState(false); - const [search, setSearch] = useState(''); - const ref = useRef(null); - const inputRef = useRef(null); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (ref.current && !ref.current.contains(event.target as Node)) { - setIsOpen(false); - setSearch(''); - } - }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); - }, []); - - // Focus input when dropdown opens - useEffect(() => { - if (isOpen) inputRef.current?.focus(); - }, [isOpen]); - - const filteredOptions = options.filter(option => { - const label = renderOption ? renderOption(option) : option; - return label.toLowerCase().includes(search.toLowerCase()); - }); - - const selectedLabel = value ? (renderOption ? renderOption(value) : value) : `Select ${label}`; - - const handleToggle = () => { - setIsOpen(prev => { - if (prev) setSearch(''); - return !prev; - }); - }; - - return ( -
    -
    - {isOpen ? ( - setSearch(e.target.value)} - placeholder={`Search ${label}...`} - className="w-full bg-transparent outline-none text-black placeholder:text-[#00000050] font-semibold" - /> - ) : ( - - {selectedLabel} - - )} - -
    - - -
    - - {isOpen && ( -
      - {filteredOptions.length > 0 ? ( - filteredOptions.map((option, index) => ( -
    • { - onChange(option); - setIsOpen(false); - setSearch(''); - }} - className={` - px-4 py-2 cursor-pointer hover:bg-[#FFEA79] - ${value === option ? 'bg-[#C1FF83] font-bold' : ''} - `} - > - {renderOption ? renderOption(option) : option} -
    • - )) - ) : ( -
    • No results
    • - )} -
    - )} -
    - ); -} - type SubjectEntry = { slot: string; faculty: string; From 0e40551ed5aa638d5d132973f32613bad603d4f7 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Mon, 22 Jun 2026 09:09:34 +0530 Subject: [PATCH 5/5] fixed ESLint warns and errors --- src/components/cards/FacultySelector.tsx | 2 +- src/components/ui/ComboBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/cards/FacultySelector.tsx b/src/components/cards/FacultySelector.tsx index a48aeb6..80453e9 100644 --- a/src/components/cards/FacultySelector.tsx +++ b/src/components/cards/FacultySelector.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import Image from 'next/image'; import { ZButton } from '../ui/Buttons'; diff --git a/src/components/ui/ComboBox.tsx b/src/components/ui/ComboBox.tsx index c65c5e7..b8d49fe 100644 --- a/src/components/ui/ComboBox.tsx +++ b/src/components/ui/ComboBox.tsx @@ -39,7 +39,7 @@ export default function ComboBox({ label, value, options, onChange, renderOption useEffect(() => { if (value) setInputValue(renderOption ? renderOption(value) : value); - }, [value]); + }, [value, renderOption]); return (