Skip to content

Commit a10760c

Browse files
authored
feature: 클래스 리스트 페이지 기능 구현 (API MOCK) (#59)
1 parent 2f34aaa commit a10760c

File tree

5 files changed

+134
-8
lines changed

5 files changed

+134
-8
lines changed
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
'use client';
22

33
import { ClassListPage } from '@/src/views/class-list';
4+
import { Suspense } from 'react';
45

56
export default function Page() {
6-
return <ClassListPage />;
7+
return (
8+
<Suspense fallback={<div>Loading...</div>}>
9+
<ClassListPage />
10+
</Suspense>
11+
);
712
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type SortType = 'popular' | 'recent';

frontend/zicdding-class.com/src/views/class-list/ui/index.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,23 @@ import { Tabs } from '@zicdding-web/ui/Tabs';
55
import { Input } from '@zicdding-web/ui/Input';
66
import { Button } from '@zicdding-web/ui';
77
import { ClassCard, useGetClasses } from '@/src/features/class-card';
8+
import type { SortType } from '../model/type';
9+
import { useSearchClass } from './use-search-class';
810
import { useRouter } from 'next/navigation';
11+
import { useMemo } from 'react';
912

1013
export function ClassListPage() {
11-
const { data: classList, isLoading } = useGetClasses();
1214
const router = useRouter();
1315

14-
// GYU-TODO: 인기순/최신순 query 로 관리하고 데이터가 없으면 기본값 설정해서 tabs 와 데이터 일치하게 하기!
15-
// tabs 도 상태로 해야하나? 아니면 onChange 가 발생할때 url 로 제어해서 따로 client-component 로 추출하지 않게 해야하나?
16-
// URL 로 제어하는게 좋을듯
16+
const { data: classList, isLoading } = useGetClasses();
17+
18+
const { localSearchValue, searchValue, sortType, onSearchSubmit, onChangeSearchValue, onSearch, onSortTypeChange } =
19+
useSearchClass();
20+
21+
const _classList = useMemo(() => {
22+
return classList?.filter((classItem) => classItem.classTitle.includes(searchValue));
23+
}, [classList, searchValue]);
24+
1725
return (
1826
<div className="px-6">
1927
<div className="flex justify-between items-center">
@@ -23,7 +31,8 @@ export function ClassListPage() {
2331
{ title: '인기순', value: 'popular' },
2432
{ title: '최신순', value: 'recent' },
2533
]}
26-
onChange={() => {}}
34+
value={sortType}
35+
onChange={(value) => onSortTypeChange(value as SortType)}
2736
defaultValue="popular"
2837
/>
2938
</div>
@@ -32,14 +41,23 @@ export function ClassListPage() {
3241

3342
<div className="flex justify-between items-center">
3443
<Button>클래스 만들기</Button>
35-
<Input type="search" width={366} onClickSearch={() => {}} />
44+
<form onSubmit={onSearchSubmit}>
45+
<Input
46+
type="search" //
47+
name="search"
48+
width={366}
49+
value={localSearchValue}
50+
onChange={onChangeSearchValue}
51+
onClickSearch={onSearch}
52+
/>
53+
</form>
3654
</div>
3755

3856
<ul className="flex flex-wrap items-start gap-8 mx-auto mt-8">
3957
{isLoading ? (
4058
<div>loading...</div>
4159
) : (
42-
classList?.map((classItem) => (
60+
_classList?.map((classItem) => (
4361
<ClassCard
4462
key={classItem.classId}
4563
title={classItem.classTitle}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useRouter, useSearchParams } from 'next/navigation';
2+
import type { SortType } from '../model/type';
3+
import { useEffect, useState } from 'react';
4+
5+
export function useSearchClass() {
6+
const router = useRouter();
7+
const searchParams = useSearchParams();
8+
9+
const [localSearchValue, setLocalSearchValue] = useState('');
10+
11+
const sortType = searchParams.get('sortType') ?? 'popular';
12+
const searchValue = searchParams.get('search') ?? '';
13+
14+
const onSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => {
15+
e.preventDefault();
16+
17+
const formData = new FormData(e.target as HTMLFormElement);
18+
const searchValue = formData.get('search');
19+
if (typeof searchValue === 'string') {
20+
onSearch(searchValue);
21+
}
22+
};
23+
24+
const onSearch = (searchValue: string) => {
25+
router.replace(`/class?sortType=${sortType}&search=${searchValue}`);
26+
};
27+
28+
const onSortTypeChange = (sortTypeValue: SortType) => {
29+
router.replace(`/class?sortType=${sortTypeValue}&search=${searchValue}`);
30+
};
31+
32+
const onChangeSearchValue = (e: React.ChangeEvent<HTMLInputElement>) => {
33+
setLocalSearchValue(e.target.value);
34+
};
35+
36+
useEffect(
37+
function mountedSyncQueryValueToLocalValue() {
38+
setLocalSearchValue(searchValue);
39+
},
40+
[searchValue],
41+
);
42+
43+
return {
44+
localSearchValue,
45+
searchValue,
46+
sortType,
47+
onSearchSubmit,
48+
onSearch,
49+
onSortTypeChange,
50+
onChangeSearchValue,
51+
};
52+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useRouter, useSearchParams } from 'next/navigation';
2+
import type { SortType } from '../model/type';
3+
import { useEffect, useState } from 'react';
4+
5+
export function useSearch() {
6+
const router = useRouter();
7+
const searchParams = useSearchParams();
8+
9+
const [localSearchValue, setLocalSearchValue] = useState('');
10+
11+
const sortType = (searchParams.get('sortType') as SortType) ?? 'popular';
12+
const searchValue = searchParams.get('search') ?? '';
13+
14+
const onSearchSubmit = (e: React.FormEvent<HTMLFormElement>) => {
15+
e.preventDefault();
16+
17+
const formData = new FormData(e.target as HTMLFormElement);
18+
const _searchValue = formData.get('search') as string;
19+
onSearch(_searchValue);
20+
};
21+
22+
const onSearch = (searchValue: string) => {
23+
router.replace(`/class?sortType=${sortType}&search=${searchValue}`);
24+
};
25+
26+
const onSortTypeChange = (sortTypeValue: SortType) => {
27+
router.replace(`/class?sortType=${sortTypeValue}&search=${searchValue}`);
28+
};
29+
30+
const onChangeSearchValue = (e: React.ChangeEvent<HTMLInputElement>) => {
31+
setLocalSearchValue(e.target.value);
32+
};
33+
34+
useEffect(
35+
function mountedSyncQueryValueToLocalValue() {
36+
setLocalSearchValue(searchValue);
37+
},
38+
[searchValue],
39+
);
40+
41+
return {
42+
localSearchValue,
43+
searchValue,
44+
sortType,
45+
onSearchSubmit,
46+
onSearch,
47+
onSortTypeChange,
48+
onChangeSearchValue,
49+
};
50+
}

0 commit comments

Comments
 (0)