-
Notifications
You must be signed in to change notification settings - Fork 2
Feat : 투표 게시판 리스트 페이지 구현 #102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f12361f
fe752f2
c775561
35a8306
7dba8c0
cdb49f6
a654e8b
2434681
9848f3e
102d902
da5dad7
05d175f
9f86c8c
86dfcc9
2188c0c
5bc2f22
ed042f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,3 +153,5 @@ coverage/ | |
| .vscode/ | ||
| .vscode/settings.json | ||
|
|
||
| #monorepo issues | ||
| monorepo-docs/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { GetVotePostListStatus } from '@/generated/api/models'; | ||
| import { TabItem } from '@/types/tab.types'; | ||
|
|
||
| export type VoteState = GetVotePostListStatus | null; | ||
|
|
||
| export const VOTE_STATES: TabItem<VoteState>[] = [ | ||
| { label: '진행중', value: 'IN_PROGRESS' }, | ||
| { label: '완료', value: 'COMPLETED' }, | ||
| ]; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| 'use client'; | ||
|
|
||
| import { useState } from 'react'; | ||
| import { useOverlay } from '@/hooks/ui/useOverlay'; | ||
| import { SortHeader } from '../components/SortHeader'; | ||
| import { SORT_OPTIONS } from '../constants/sortOptions'; | ||
| import { SortValue } from '@/types/options.types'; | ||
| import { PillChipsTab } from '@/components/tabs/PillChipsTab'; | ||
| import { CATEGORIES } from '../constants/categories'; | ||
| import { VOTE_STATES, VoteState } from '../constants/votesOptions'; | ||
| import { VoteboardSummary } from '@/generated/api/models'; | ||
| import FloatingCategoryMenu from '@/components/buttons/FloatingCategoryMenu'; | ||
| import CommunityPostList from '../components/CommunityPostList'; | ||
| import { VoteBoardCard } from './components/VoteBoardCard'; | ||
| import { | ||
| getVotePostsByCursor, | ||
| getGetVotePostsByCursorQueryKey, | ||
| } from '@/generated/api/endpoints/voteboard/voteboard'; | ||
| import { useInfiniteQuery } from '@tanstack/react-query'; | ||
| import FloatingButton from '@/components/buttons/FloatingButton'; | ||
| /** | ||
| * 투표 게시판 클라이언트 메인 페이지 | ||
| * | ||
| * @description | ||
| * - [전체/진행중/완료] 상태 탭 제공 | ||
| * - 상태별 투표 게시글 목록 표시 | ||
| * - 정렬 옵션 제공 | ||
| * | ||
| */ | ||
|
|
||
| export default function VotesboardClientPage() { | ||
| const [sortOption, setSortOption] = useState<SortValue>('LATEST'); | ||
| const [voteState, setVoteState] = useState<VoteState>(null); | ||
| const { open } = useOverlay(); | ||
| // 무한스크롤 데이터 페칭 | ||
| const { | ||
| data, | ||
| fetchNextPage, | ||
| hasNextPage, | ||
| isLoading, | ||
| isFetchingNextPage, | ||
| error, | ||
| refetch, | ||
| } = useInfiniteQuery({ | ||
| queryKey: getGetVotePostsByCursorQueryKey({ | ||
| status: voteState ?? undefined, | ||
| sort: sortOption, | ||
| }), | ||
| queryFn: ({ pageParam, signal }) => | ||
| getVotePostsByCursor( | ||
| { | ||
| status: voteState ?? undefined, | ||
| sort: sortOption, | ||
| cursor: pageParam, | ||
| size: 10, | ||
| }, | ||
| signal, | ||
| ), | ||
| initialPageParam: undefined as string | undefined, | ||
| getNextPageParam: (lastPage) => { | ||
| return lastPage.hasNext ? lastPage.nextCursor : undefined; | ||
| }, | ||
| }); | ||
| const allVotePosts: VoteboardSummary[] = | ||
| data?.pages.flatMap((page) => page.posts ?? []) ?? []; | ||
| const totalCount = data?.pages[0]?.totalCount ?? 0; | ||
|
|
||
| const handleFloatingButtonClick = () => { | ||
| open( | ||
| ({ close }) => ( | ||
| <FloatingCategoryMenu | ||
| route="votesboard" | ||
| categories={CATEGORIES} | ||
| onClose={() => close(null, { duration: 200 })} | ||
| /> | ||
| ), | ||
| { | ||
| backdrop: true, | ||
| closeOnBackdrop: true, | ||
| }, | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
| <main className="w-full h-full flex flex-col"> | ||
| <PillChipsTab<VoteState> | ||
| chips={VOTE_STATES} | ||
| showAll | ||
| activeValue={voteState} | ||
| onChange={setVoteState} | ||
| ariaLabel="투표 상태 선택 필터" | ||
| /> | ||
| {/* 필터 헤더 */} | ||
| <SortHeader | ||
| totalCount={totalCount} | ||
| sortOptions={SORT_OPTIONS} | ||
| currentValue={sortOption} | ||
| onFilterChange={setSortOption} | ||
| /> | ||
|
|
||
| <CommunityPostList<VoteboardSummary> | ||
| items={allVotePosts} | ||
| hasNextPage={hasNextPage || false} | ||
| fetchNextPage={fetchNextPage} | ||
| isFetchingNextPage={isFetchingNextPage} | ||
| initialLoading={isLoading} | ||
| error={error} | ||
| onRetry={() => refetch()} | ||
| storageKey="votesboard-post-list-scroll" | ||
| getItemKey={(post, index) => post.postId ?? `post-${index}`} | ||
| renderItem={(post) => <VoteBoardCard post={post} />} | ||
| /> | ||
|
|
||
| {/* TODO: FloatingButton 추가 */} | ||
| <FloatingButton onClick={handleFloatingButtonClick} /> | ||
| </main> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,104 @@ | ||||||||||||||
| // src/components/CommunityCard.tsx | ||||||||||||||
|
||||||||||||||
| // src/components/CommunityCard.tsx | |
| // src/app/main/community/votesboard/components/VoteBoardCard.tsx |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 처리된 import와 코드를 제거해야 합니다. 추후 구현 예정인 기능이라면, 구현 시점에 추가하는 것이 코드 가독성과 유지보수 측면에서 더 좋습니다.
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 처리된 코드를 제거해야 합니다. 추후 구현 예정인 기능이라면, 구현 시점에 추가하는 것이 더 좋습니다. 또한 console.warn 대신 실제 동작을 구현하거나, 임시 구현이라면 주석으로만 남기는 것을 권장합니다.
| console.warn( | |
| '추후 투표 상세 페이지로 이동할 예정입니다. => postId:', | |
| postId, | |
| ); | |
| //router.push(`/main/community/votesboard/${postId}`); | |
| // TODO: 투표 상세 페이지로 이동 기능은 추후 구현 예정입니다. |
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클래스명에 불필요한 공백이 있습니다. 'w-4 h-4 ' 대신 'w-4 h-4'로 수정해야 합니다.
| 'w-4 h-4 ', | |
| 'w-4 h-4', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO 주석이 이미 완료된 작업을 가리키고 있습니다. FloatingButton이 이미 115번 라인에 구현되어 있으므로 이 TODO 주석을 제거해야 합니다.