From 31add94caf111ba8aadb80c8a905ffc7309669a7 Mon Sep 17 00:00:00 2001 From: tree105778 Date: Wed, 15 Oct 2025 21:07:16 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=84=A4=EC=A0=95=EC=B0=BD=20?= =?UTF-8?q?=ED=95=99=EA=B3=BC=20=EB=A9=80=ED=8B=B0=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/settings-dropdown.tsx | 260 ++++++++++++++++++------ 1 file changed, 199 insertions(+), 61 deletions(-) diff --git a/src/components/ui/settings-dropdown.tsx b/src/components/ui/settings-dropdown.tsx index 1297345..7573144 100644 --- a/src/components/ui/settings-dropdown.tsx +++ b/src/components/ui/settings-dropdown.tsx @@ -7,7 +7,7 @@ import universityData from '@/constants/university.json'; interface SettingsDropdownProps { open: boolean; onClose: () => void; - onChange?: (payload: { university: string; department: string }) => void; + onChange?: (payload: { departments: string[] }) => void; } const DROPDOWN_STORAGE_KEY = 'uosask-settings'; @@ -16,7 +16,7 @@ const Container = styled.div` position: absolute; top: 90px; /* below header */ right: 40px; - width: 360px; + width: 800px; /* increased width for horizontal layout */ background-color: ${({ theme }) => theme.colors.background}; border: 1px solid ${({ theme }) => theme.colors.borderLight}; border-radius: ${({ theme }) => theme.radii.md}; @@ -69,23 +69,141 @@ const Button = styled.button` cursor: pointer; `; +const CheckboxContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.xs}; + max-height: 200px; + overflow-y: auto; + border: 1px solid ${({ theme }) => theme.colors.borderLight}; + border-radius: ${({ theme }) => theme.radii.sm}; + padding: ${({ theme }) => theme.spacing.sm}; + background-color: ${({ theme }) => theme.colors.backgroundButton}; +`; + +const CheckboxItem = styled.label` + display: flex; + align-items: center; + gap: ${({ theme }) => theme.spacing.xs}; + cursor: pointer; + font-size: ${({ theme }) => theme.fontSizes.sm}; + color: ${({ theme }) => theme.colors.text}; +`; + +const Checkbox = styled.input` + width: 16px; + height: 16px; + cursor: pointer; +`; + +const SelectAllContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: ${({ theme }) => theme.spacing.md}; + padding: ${({ theme }) => theme.spacing.sm}; + background-color: ${({ theme }) => theme.colors.backgroundButton}; + border-radius: ${({ theme }) => theme.radii.sm}; + border: 1px solid ${({ theme }) => theme.colors.borderLight}; +`; + +const SelectAllButton = styled.button` + font-size: ${({ theme }) => theme.fontSizes.sm}; + color: ${({ theme }) => theme.colors.text}; + background: none; + border: none; + cursor: pointer; + font-weight: 500; +`; + +const SelectedCount = styled.span` + font-size: ${({ theme }) => theme.fontSizes.sm}; + color: ${({ theme }) => theme.colors.textSecondary}; +`; + +const SearchInput = styled.input` + width: 100%; + height: 40px; + border: 1px solid ${({ theme }) => theme.colors.borderLight}; + border-radius: ${({ theme }) => theme.radii.sm}; + background-color: ${({ theme }) => theme.colors.backgroundButton}; + color: ${({ theme }) => theme.colors.text}; + padding: 0 ${({ theme }) => theme.spacing.sm}; + font-size: ${({ theme }) => theme.fontSizes.sm}; + margin-bottom: ${({ theme }) => theme.spacing.md}; + + &::placeholder { + color: ${({ theme }) => theme.colors.textSecondary}; + } + + &:focus { + outline: none; + border-color: ${({ theme }) => theme.colors.primary}; + } +`; + +const HorizontalLayout = styled.div` + display: flex; + gap: ${({ theme }) => theme.spacing.lg}; + overflow-x: auto; + padding-bottom: ${({ theme }) => theme.spacing.sm}; +`; + +const UniversityColumn = styled.div` + min-width: 200px; + flex-shrink: 0; +`; + +const UniversityTitle = styled.h4` + font-size: ${({ theme }) => theme.fontSizes.sm}; + font-weight: 600; + color: ${({ theme }) => theme.colors.text}; + margin-bottom: ${({ theme }) => theme.spacing.sm}; + padding: ${({ theme }) => theme.spacing.sm}; + background-color: ${({ theme }) => theme.colors.backgroundButton}; + border-radius: ${({ theme }) => theme.radii.sm}; + border: 1px solid ${({ theme }) => theme.colors.borderLight}; + text-align: center; +`; + +const DepartmentList = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing.xs}; + max-height: 300px; + overflow-y: auto; + padding: ${({ theme }) => theme.spacing.sm}; + background-color: ${({ theme }) => theme.colors.backgroundButton}; + border-radius: ${({ theme }) => theme.radii.sm}; + border: 1px solid ${({ theme }) => theme.colors.borderLight}; +`; + export function SettingsDropdown({ open, onClose, onChange, }: SettingsDropdownProps) { - const universities = useMemo( - () => universityData.map((u) => u.university), - [], - ); - const departmentMap = useMemo(() => { - const map = new Map(); - universityData.forEach((u) => map.set(u.university, u.departments)); - return map; + const departmentGroups = useMemo(() => { + return universityData.map((u) => ({ + university: u.university, + departments: u.departments.sort(), + })); }, []); - const [university, setUniversity] = useState(''); - const [department, setDepartment] = useState(''); + const allDepartments = useMemo(() => { + const departments: string[] = []; + universityData.forEach((u) => { + u.departments.forEach((dept) => { + if (!departments.includes(dept)) { + departments.push(dept); + } + }); + }); + return departments.sort(); + }, []); + + const [departments, setDepartments] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { if (!open) return; @@ -93,34 +211,24 @@ export function SettingsDropdown({ const raw = localStorage.getItem(DROPDOWN_STORAGE_KEY); if (raw) { const parsed = JSON.parse(raw) as { - university?: string; - department?: string; + departments?: string[]; }; - if (parsed.university && universities.includes(parsed.university)) { - setUniversity(parsed.university); - const deps = departmentMap.get(parsed.university) ?? []; - if (parsed.department && deps.includes(parsed.department)) { - setDepartment(parsed.department); - } else { - setDepartment(''); - } + if (parsed.departments && Array.isArray(parsed.departments)) { + const validDepartments = parsed.departments.filter((dep) => + allDepartments.includes(dep), + ); + setDepartments(validDepartments); + } else { + setDepartments([]); } } } catch {} - }, [open, universities, departmentMap]); - - useEffect(() => { - if (!university) return; - const deps = departmentMap.get(university) ?? []; - if (!deps.includes(department)) { - setDepartment(''); - } - }, [university, department, departmentMap]); + }, [open, allDepartments]); if (!open) return null; const handleSave = () => { - const payload = { university, department }; + const payload = { departments }; try { localStorage.setItem(DROPDOWN_STORAGE_KEY, JSON.stringify(payload)); } catch {} @@ -128,41 +236,71 @@ export function SettingsDropdown({ onClose(); }; - const availableDepartments = departmentMap.get(university) ?? []; + const handleDepartmentToggle = (department: string) => { + setDepartments((prev) => + prev.includes(department) + ? prev.filter((dep) => dep !== department) + : [...prev, department], + ); + }; + + const handleSelectAll = () => { + if (departments.length === allDepartments.length) { + setDepartments([]); + } else { + setDepartments([...allDepartments]); + } + }; + + const filteredDepartmentGroups = !searchTerm.trim() + ? departmentGroups + : departmentGroups + .map((group) => ({ + ...group, + departments: group.departments.filter((dept) => + dept.toLowerCase().includes(searchTerm.toLowerCase()), + ), + })) + .filter((group) => group.departments.length > 0); return ( 설정 - - - - - - +