Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 6 additions & 83 deletions src/components/cards/FacultySelector.tsx
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
'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';

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',
Expand All@@ -33,83 +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 ref = useRef<HTMLDivElement>(null);

// Close dropdown on outside click
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);

const selectedLabel = value ? (renderOption ? renderOption(value) : value) : `Select ${label}`;

return (
<div ref={ref} className="relative w-full font-semibold text-[#000000B2]">
<button
onClick={() => setIsOpen(!isOpen)}
title={selectedLabel}
aria-label={`Select ${label}`}
className={`
w-full h-10 pl-3 pr-12 text-left bg-white rounded-xl border-3 border-black
cursor-pointer relative
${!value ? 'text-[#00000080]' : 'text-black'}
truncate whitespace-nowrap overflow-hidden
`}
>
{selectedLabel}
<div className="absolute right-11 top-0 h-full w-[3px] bg-black" />
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
<Image
src="/icons/chevron_down.svg"
alt="icon"
className="w-5 h-5"
width={20}
height={20}
unselectable="on"
draggable={false}
priority
/>
</div>
</button>

{isOpen && (
<ul className="absolute -left-7 -right-7 z-10 bg-white border-3 border-black rounded-xl mt-1 max-h-120 overflow-y-auto shadow-lg">
{options.map((option, index) => (
<li
key={index}
onClick={() => {
onChange(option);
setIsOpen(false);
}}
className={`
px-4 py-2 cursor-pointer hover:bg-[#FFEA79]
${value === option ? 'bg-[#C1FF83] font-bold' : ''}
`}
>
{renderOption ? renderOption(option) : option}
</li>
))}
</ul>
)}
</div>
);
}

type SubjectEntry = {
slot: string;
faculty: string;
Expand DownExpand Up@@ -692,22 +615,22 @@ export default function FacultySelector({
</div>

<div className="grid grid-cols-3 gap-4 m-4 px-4">
<SelectField
<ComboBox
label={'Domain'}
value={selectedDomain}
options={domains}
onChange={handleDomainChange}
renderOption={prettifyDomain}
/>
<SelectField
<ComboBox
label="Subject"
value={selectedSubject}
options={subjects}
onChange={handleSubjectChange}
/>
{getCourseType(selectedSubject.split(' - ')[0]) === 'P' &&
!selectedSubject.split(' - ')[0].startsWith('BSTS') ? (
<SelectField
<ComboBox
label="Slot"
value={selectedLabShift}
onChange={e => {
Expand All@@ -722,7 +645,7 @@ export default function FacultySelector({
)}
/>
) : (
<SelectField
<ComboBox
label="Slot"
value={selectedSlot}
options={[...slots].sort((a, b) => a.localeCompare(b))}
Expand Down
21 changes: 12 additions & 9 deletions src/components/ui/ComboBox.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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<HTMLDivElement>(null);
Expand All@@ -24,19 +25,21 @@ 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);
}, [value]);
if (value) setInputValue(renderOption ? renderOption(value) : value);
}, [value, renderOption]);

return (
<div ref={ref} className="relative w-full font-semibold text-[#000000B2]">
Expand DownExpand Up@@ -82,7 +85,7 @@ export default function ComboBox({ label, value, options, onChange }: ComboBoxPr
</div>

{isOpen && (
<ul className="absolute left-0 right-0 z-10 bg-white border-3 border-black rounded-xl mt-1 max-h-120 overflow-y-auto shadow-lg">
<ul className="absolute -left-2 -right-2 z-10 bg-white border-3 border-black rounded-xl mt-1 max-h-120 overflow-y-auto shadow-lg">
{filteredOptions.map((option, index) => (
<li
key={index}
Expand All@@ -92,7 +95,7 @@ export default function ComboBox({ label, value, options, onChange }: ComboBoxPr
${value === option ? 'bg-[#C1FF83] font-bold' : ''}
`}
>
{option}
{renderOption ? renderOption(option) : option}
</li>
))}
</ul>
Expand Down