Skip to content

Commit 39c1d1b

Browse files
authored
Merge pull request #145 from ezcode-my/refactor/grimza99
fix : 자동완성, 검색
2 parents f4eea85 + 7eb051e commit 39c1d1b

File tree

16 files changed

+390
-425
lines changed

16 files changed

+390
-425
lines changed

src/app/(auth)/(navigationsBarLayout)/problems/page.tsx

Lines changed: 59 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,21 @@
11
'use client';
22

3-
import { useEffect, useState } from 'react';
3+
import { CompositionEvent, useEffect, useState } from 'react';
44
import Image from 'next/image';
5-
import ProblemTable from './ui/Table';
6-
import { useProblemListQuery } from '@/entities/problems/model/query';
5+
import { useAutoCompleteKeywordQuery, useProblemListQuery } from '@/entities/problems/model/query';
76
import { Select } from '@/shared/ui/select/Select';
87
import { Button } from '@/shared/ui/button/Button';
98
import { LevelUtil } from '@/shared/util/levelUtil';
10-
11-
const categoryCodeOptions = [
12-
{ label: '출력', value: 'OUTPUT' },
13-
{ label: '사칙연산', value: 'ARITHMETIC' },
14-
{ label: '배열', value: 'ARRAY' },
15-
{ label: '조건문', value: 'CONDITIONAL' },
16-
{ label: '정렬', value: 'SORTING' },
17-
{ label: '수학', value: 'MATH' },
18-
{ label: '시뮬레이션', value: 'SIMULATION' },
19-
{ label: '자료구조', value: 'DATA_STRUCTURE' },
20-
{ label: '입문자용', value: 'FOR_BEGINNER' },
21-
{ label: '구현', value: 'IMPLEMENTATION' },
22-
{ label: '그리디', value: 'GREEDY' },
23-
{ label: '문자열 처리', value: 'STRING_PROCESSING' },
24-
{ label: '해시맵', value: 'HASH_MAP' },
25-
{ label: '선형 탐색', value: 'LINEAR_SEARCH' },
26-
{ label: '동적 계획법', value: 'DP' },
27-
{ label: '순환 탐지', value: 'CYCLE_DETECTION' },
28-
{ label: '비트 연산', value: 'BIT_OPERATION' },
29-
{ label: '반복 제어', value: 'LOOP_CONTROL' },
30-
{ label: '카운팅', value: 'COUNTING' },
31-
{ label: '너비 우선 탐색', value: 'BFS' },
32-
{ label: '깊이 우선 탐색', value: 'DFS' },
33-
{ label: '비트마스킹', value: 'BITMASK' },
34-
{ label: '해시', value: 'HASH' },
35-
{ label: '맵', value: 'MAP' },
36-
{ label: '반복문', value: 'LOOPS' },
37-
{ label: '분할 정복', value: 'DIVIDE_AND_CONQUER' },
38-
{ label: '문자열', value: 'STRING' },
39-
{ label: '집합론', value: 'SET_THEORY' },
40-
{ label: '누적 합', value: 'PREFIX_SUM' },
41-
{ label: '기하학', value: 'GEOMETRY' },
42-
{ label: '이분 탐색', value: 'BINARY_SEARCH' },
43-
{ label: '투포인터', value: 'TWO_POINTERS' },
44-
{ label: '그래프 이론', value: 'GRAPH_THEORY' },
45-
{ label: '탐색', value: 'SEARCH' },
46-
{ label: '우선순위 큐', value: 'PRIORITY_QUEUE' },
47-
{ label: '백트래킹', value: 'BACKTRACKING' },
48-
{ label: '알고리즘', value: 'ALGORITHM' },
49-
{ label: '트리', value: 'TREE' },
50-
{ label: '상태 압축', value: 'STATE_COMPRESSION' },
51-
{ label: '재귀', value: 'RECURSION' },
52-
{ label: '큐', value: 'QUEUE' },
53-
{ label: '최대 유량', value: 'MAX_FLOW' },
54-
{ label: '최소 컷', value: 'MIN_CUT' },
55-
{ label: '최소 스패닝 트리', value: 'MINIMUM_SPANNING_TREE' },
56-
{ label: '완전 탐색', value: 'BRUTE_FORCE' },
57-
{ label: '조합론', value: 'COMBINATORICS' },
58-
{ label: '세그먼트 트리', value: 'SEGMENT_TREE' },
59-
{ label: 'Deque', value: 'DEQUE' },
60-
{ label: '해밍 거리', value: 'HAMMING_DISTANCE' },
61-
{ label: '2차원 배열', value: 'TWO_DIMENSIONAL_ARRAY' },
62-
{ label: '누적 선택 최적화', value: 'CUMULATIVE_SELECTION_OPTIMIZATION' },
63-
{ label: '좌표', value: 'COORDINATE' },
64-
{ label: '최대공약수(GCD)', value: 'GCD' },
65-
{ label: '수열', value: 'SEQUENCE' },
66-
{ label: '집합 처리', value: 'SET_PROCESSING' },
67-
{ label: '그래프 탐색', value: 'GRAPH_SEARCH' },
68-
{ label: '분리 집합', value: 'DISJOINT_SET' },
69-
{ label: '조합', value: 'COMBINATION' },
70-
];
9+
import ProblemTable from '@/entities/problems/ui/table/ProblemTable';
10+
import { CATEGORY_OPTIONS, DIFFICULTY_OPTIONS } from '@/entities/problems/model/filter-options';
7111

7212
const ProblemsList = () => {
7313
const [currentPage, setCurrentPage] = useState(0);
74-
75-
const difficultyOptions = [
76-
{ label: '입문 (1단계)', value: 'LV1' },
77-
{ label: '초급 (2단계)', value: 'LV2' },
78-
{ label: '중급 (3단계)', value: 'LV3' },
79-
{ label: '중상급 (4단계)', value: 'LV4' },
80-
{ label: '고급 (5단계)', value: 'LV5' },
81-
{ label: '전문가 (6단계)', value: 'LV6' },
82-
{ label: '마스터 (7단계)', value: 'LV7' },
83-
];
8414
const [categoryCode, setCategoryCode] = useState('');
8515
const [difficulty, setDifficulty] = useState('');
8616
const [keyword, setKeyword] = useState('');
8717
const [search, setSearch] = useState('');
18+
const [autoCompleteKeyword, setAutoCompleteKeyword] = useState('');
8819

8920
const { data, isLoading } = useProblemListQuery(
9021
currentPage,
@@ -95,11 +26,27 @@ const ProblemsList = () => {
9526
search
9627
);
9728

29+
const { data: autoComplete } = useAutoCompleteKeywordQuery(autoCompleteKeyword);
9830
const totalPages = data?.totalPages ?? 0;
9931

10032
useEffect(() => {
10133
setCurrentPage(0);
10234
}, [categoryCode, difficulty]);
35+
36+
const handleCompositionEnd = (e: CompositionEvent<HTMLInputElement>) => {
37+
const value = e.currentTarget.value;
38+
if (value.length >= 2 && value.length <= 25) {
39+
setAutoCompleteKeyword(value);
40+
}
41+
};
42+
43+
const handleChangeKeyword = (value: string) => {
44+
if (!value) {
45+
setAutoCompleteKeyword('');
46+
}
47+
setKeyword(value);
48+
};
49+
10350
return (
10451
<div className="flex flex-col px-10 py-18 w-full gap-4 justify-center items-center">
10552
<div className="flex flex-col max-w-[1600px] w-full gap-6">
@@ -108,6 +55,7 @@ const ProblemsList = () => {
10855
<p className="text-gray-400">코딩테스트 문제를 난이도별로 확인하고 도전해보세요</p>
10956
</section>
11057

58+
{/* 필터영역 */}
11159
<section className="flex flex-col gap-10">
11260
<section className="mb-6 p-6 bg-gray-900/50 rounded-[10px] border border-gray-800">
11361
<div className="flex flex-row gap-4 items-center w-full">
@@ -119,7 +67,7 @@ const ProblemsList = () => {
11967
id="category"
12068
className="w-full"
12169
title="카테고리"
122-
option={categoryCodeOptions}
70+
option={CATEGORY_OPTIONS}
12371
setValue={(value) => setCategoryCode(value)}
12472
value={categoryCode}
12573
/>
@@ -133,7 +81,7 @@ const ProblemsList = () => {
13381
id="difficulty"
13482
className="w-full "
13583
title="난이도"
136-
option={difficultyOptions}
84+
option={DIFFICULTY_OPTIONS}
13785
setValue={(value) => setDifficulty(value)}
13886
value={difficulty}
13987
/>
@@ -142,18 +90,39 @@ const ProblemsList = () => {
14290
<div className="flex flex-col gap-1 w-1/3">
14391
<label className="text-base text-secondary">검색</label>
14492
<div className="flex flex-row w-full gap-5">
145-
<input
146-
placeholder="문제 제목 또는 번호 검색"
147-
className="text-base border px-2 border-gray-700 rounded h-12 w-full bg-gray-800"
148-
onChange={(e) => setKeyword(e.target.value)}
149-
onKeyDown={(e) => {
150-
if (e.key === 'Enter') {
151-
setSearch(keyword);
152-
setCurrentPage(0); // 검색하면 0페이지(첫페이지)로
153-
}
154-
}}
155-
/>
156-
93+
<div className="relative flex-grow">
94+
<input
95+
value={keyword}
96+
placeholder="2~25글자 사이로 검색해주세요"
97+
className="text-base border px-2 border-gray-700 rounded h-12 w-full bg-gray-800"
98+
onChange={(e) => handleChangeKeyword(e.target.value)}
99+
onCompositionEnd={handleCompositionEnd}
100+
onKeyDown={(e) => {
101+
if (e.key === 'Enter') {
102+
setSearch(keyword);
103+
setCurrentPage(0); // 검색하면 0페이지(첫페이지)로
104+
}
105+
}}
106+
/>
107+
{autoComplete && autoComplete.length > 0 && (
108+
<div className="absolute top-14 border px-2 border-gray-700 rounded bg-gray-800 w-full">
109+
{autoComplete.map((item) => (
110+
<p
111+
key={item}
112+
className="py-1 hover:bg-gray-700 cursor-pointer"
113+
onClick={() => {
114+
setKeyword(item);
115+
setSearch(item);
116+
setAutoCompleteKeyword('');
117+
setCurrentPage(0); // 검색하면 0페이지로
118+
}}
119+
>
120+
{item}
121+
</p>
122+
))}
123+
</div>
124+
)}
125+
</div>
157126
<Button
158127
aria-label="검색"
159128
onClick={() => {
@@ -189,9 +158,7 @@ const ProblemsList = () => {
189158
>
190159
{categoryCode !== '전체' && categoryCode && (
191160
<div className="flex flex-row gap-2 items-center bg-[#00d084]/20 border border-[#00d084]/30 text-[#00d084] px-3 py-1.5 rounded-full text-sm font-medium">
192-
<span>
193-
{categoryCodeOptions.find((item) => item.value === categoryCode)?.label}
194-
</span>
161+
<span>{CATEGORY_OPTIONS.find((item) => item.value === categoryCode)?.label}</span>
195162
<Image
196163
src="/icons/close/closeWithBorder.svg"
197164
className="cursor-pointer hover:opacity-70 transition-opacity"
@@ -206,7 +173,7 @@ const ProblemsList = () => {
206173
<div
207174
className={`flex flex-row gap-2 items-center px-3 py-1.5 rounded-full text-sm font-medium transition-all duration-200 border ${LevelUtil.getLevelBg(difficulty)} ${LevelUtil.getLevelColorClass(difficulty)}`}
208175
>
209-
<span>{difficultyOptions.find((item) => item.value === difficulty)?.label}</span>
176+
<span>{DIFFICULTY_OPTIONS.find((item) => item.value === difficulty)?.label}</span>
210177
<Image
211178
src="/icons/close/closeWithBorder.svg"
212179
className="cursor-pointer hover:opacity-70 transition-opacity"

0 commit comments

Comments
 (0)