-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] WTH-241 게시글 작성/수정/삭제 API 연결 #39
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
Open
nabbang6
wants to merge
41
commits into
develop
Choose a base branch
from
WTH-241-게시글-작성-수정-삭제-API-연결
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
The head ref may contain hidden characters: "WTH-241-\uAC8C\uC2DC\uAE00-\uC791\uC131-\uC218\uC815-\uC0AD\uC81C-API-\uC5F0\uACB0"
Open
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
deb0eb0
feat: 게시글 작성 API 연결
nabbang6 4b289b0
fix: 게시글 작성 관련 버그 수정
nabbang6 81598b6
feat: 게시글 목록/상세 콘텐츠 렌더링 개선
nabbang6 2b1e913
feat: 게시글 수정 API 연결
nabbang6 4303ae2
feat: 게시글 삭제 API 연결
nabbang6 09cf581
refactor: PostCardContent를 ListContent / DetailContent로 분리
nabbang6 dc723f5
refactor: 게시글 수정 페이지 헤더를 CategorySelector로 통일
nabbang6 714c71b
fix: ActionMenu 클릭 시 Link 네비게이션되는 문제 수정
nabbang6 9c1d74a
feat: 게시글 작성/수정 페이지 이탈 방지 guard 추가
nabbang6 2ae1bc0
fix: 게시글 상세 페이지 네비게이션 개선
nabbang6 997c016
style: 에디터 첨부 영역 레이아웃 개선
nabbang6 8717a94
Merge branch 'develop' of https://github.com/Team-Weeth/weeth-client …
nabbang6 9e95ac5
fix: prettier 포맷팅 수정
nabbang6 db30143
fix: 게시글 작성/수정 후 목록 캐시 갱신 및 성공 토스트 추가
nabbang6 1a67d5e
feat: 게시판 글쓰기 버튼에 기수 미입력 검증 추가
nabbang6 8beeab9
fix: fileSize와 contentType을 FileAttachment에서 매핑하게 수정
nabbang6 283fad9
fix: 게시글 수정 시 기존 서버 파일을 제외하고 새로 추가된 파일만 포함하게 수정
nabbang6 1b67286
refactor: 헤더에서 작성/수정 중 버튼 분리
nabbang6 4cc519d
refactor: validatePost 유틸로 공통 검증 로직 분리
nabbang6 f72541b
fix: 코드 스플리팅 오버헤드 수정
nabbang6 faa08e0
refactor: 내부 서브 컴포넌트 및 useLineClamp 훅을 별도 파일로 분리
nabbang6 ef211b5
fix: contentType 빈 문자열 처리 누락 문제 수정
nabbang6 aab38b0
fix: 게시글 수정 시 hasChanges 오탐 가능성 처리
nabbang6 dcf94ef
comment: 의도적인 마운트 전용 effect의 ESLint 경고 처리
nabbang6 78846af
refactor: 렌더 중 store 변경을 useEffect + ref guard 패턴으로 변경
nabbang6 dbc5814
refactor: PostCardContent 컴포넌트 분리 처리
nabbang6 a442b8f
fix: ActionMenu 영역을 Button의 onClickCapture로 전파를 막도록 변경
nabbang6 caf69d5
refactor: TitleInput 컴포넌트 분리
nabbang6 cb09526
fix: 게시글 수정 페이지에 유효하지 않은 ID 에러 처리 추가
nabbang6 bbd7776
fix: 수정 페이지에서 변경 없이 뒤로가기 시 경고 다이얼로그 표시되는 문제 수정
nabbang6 db3d446
fix: 프로필 상태 로딩 중 글쓰기 버튼 클릭 시 잘못된 모달 표시되는 문제 수정
nabbang6 8ae77f6
refactor: isHtmlEmpty를 shared에서 board 유틸로 이동
nabbang6 8c23d62
Merge branch 'develop' of https://github.com/Team-Weeth/weeth-client …
nabbang6 7849f11
fix: 게시글 목록 조회 시 이미지 카드 불러올 수 있게 수정
nabbang6 0f5e0e7
chore: 불필요한 코드 주석 삭제
nabbang6 d2b643c
feat: 게시글 목록이 null일 때 표시되는 문구 추가
nabbang6 bb6140d
fix: board 페이지에서 드롭다운 메뉴 클릭 시 기능이 수행되지 않는 문제 수정
nabbang6 901a847
fix: prettier 포맷팅 수정
nabbang6 7e358e0
style: 게시글 상세 조회 시 답글 간 간격 추가
nabbang6 74e8f3d
fix: UI 표시와 스토어 상태 간 불일치 문제 수정
nabbang6 cc5cbf7
fix: ALL/유효하지 않은 게시판 ID가 board에 그대로 남는 문제 수정
nabbang6 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/app/(private)/(main)/board/edit/[id]/EditClientEditor.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| 'use client'; | ||
|
|
||
| import { useEffect, useRef } from 'react'; | ||
|
|
||
| import { CategorySelector, PostEditorShell } from '@/components/board'; | ||
| import { useBoardList } from '@/hooks'; | ||
| import { toBoardNavItem } from '@/lib/board'; | ||
| import { usePostStore } from '@/stores/usePostStore'; | ||
| import type { PostDetail } from '@/types/board'; | ||
|
|
||
| interface EditClientEditorProps { | ||
| post: PostDetail; | ||
| } | ||
|
|
||
| function EditClientEditor({ post }: EditClientEditorProps) { | ||
| const initializedRef = useRef(false); | ||
|
|
||
| useEffect(() => { | ||
| if (initializedRef.current) return; | ||
| initializedRef.current = true; | ||
| usePostStore.getState().initFromDetail(post); | ||
| }, [post]); | ||
|
|
||
| const { data: boards } = useBoardList(); | ||
| const items = boards?.map(toBoardNavItem) ?? []; | ||
|
|
||
| const board = usePostStore((s) => s.board); | ||
| const setBoard = usePostStore((s) => s.setBoard); | ||
|
|
||
| const activeId = board ?? post.boardId; | ||
|
|
||
| return ( | ||
| <PostEditorShell | ||
| align="center" | ||
| initialContent={post.content} | ||
| header={<CategorySelector items={items} activeId={activeId} onItemSelect={setBoard} />} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export { EditClientEditor }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { notFound } from 'next/navigation'; | ||
|
|
||
| import { boardServerApi } from '@/lib/apis/board.server'; | ||
| import { EditClientEditor } from './EditClientEditor'; | ||
|
|
||
| interface PostEditPageProps { | ||
| params: Promise<{ id: string }>; | ||
| } | ||
|
|
||
| export default async function PostEditPage({ params }: PostEditPageProps) { | ||
| const { id } = await params; | ||
| const postId = Number(id); | ||
|
|
||
| if (Number.isNaN(postId)) { | ||
| notFound(); | ||
| } | ||
|
|
||
| // TODO: 추후 하드코딩된 clubId 제거 예정 | ||
| const response = await boardServerApi.getPostById('YUNJcjFKMO', postId).catch(() => null); | ||
|
|
||
| if (!response?.data) { | ||
| notFound(); | ||
| } | ||
|
|
||
| return ( | ||
| <main className="w-full"> | ||
| <EditClientEditor post={response.data} /> | ||
| </main> | ||
| ); | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,41 +1,50 @@ | ||
| 'use client'; | ||
|
|
||
| import dynamic from 'next/dynamic'; | ||
| import { useEffect } from 'react'; | ||
| import { useEffect, useLayoutEffect } from 'react'; | ||
|
|
||
| import { TitleInput, CategorySelector } from '@/components/board'; | ||
| import { CategorySelector, PostEditorShell } from '@/components/board'; | ||
| import { useBoardList } from '@/hooks'; | ||
| import { toBoardNavItem } from '@/lib/board'; | ||
| import { usePostStore } from '@/stores/usePostStore'; | ||
|
|
||
| const Editor = dynamic(() => import('@/components/board/Editor'), { ssr: false }); | ||
| import { useActiveBoardId } from '@/stores/useBoardNavStore'; | ||
|
|
||
| export default function ClientEditor() { | ||
| const { data: boards } = useBoardList(); | ||
| const items = boards?.map(toBoardNavItem) ?? []; | ||
| const writableItems = items.filter((item) => item.type !== 'ALL'); | ||
| const activeBoardId = useActiveBoardId(); | ||
|
|
||
| const board = usePostStore((s) => s.board); | ||
| const setBoard = usePostStore((s) => s.setBoard); | ||
| const reset = usePostStore((s) => s.reset); | ||
|
|
||
| const defaultId = items.find((item) => item.type === 'ALL')?.id ?? items[0]?.id ?? null; | ||
| const isWritable = (id: number | null) => | ||
| id !== null && writableItems.some((item) => item.id === id); | ||
|
|
||
| useEffect(() => { | ||
| if (board === null && defaultId !== null) { | ||
| setBoard(defaultId); | ||
| useLayoutEffect(() => { | ||
| reset(); | ||
| if (isWritable(activeBoardId)) { | ||
| setBoard(activeBoardId); | ||
| } | ||
| }, [board, defaultId, setBoard]); | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps -- 마운트 시 1회만 실행 | ||
| }, []); | ||
|
|
||
| const activeId = isWritable(board) | ||
| ? board | ||
| : isWritable(activeBoardId) | ||
| ? activeBoardId | ||
| : (writableItems[0]?.id ?? null); | ||
|
|
||
| const activeId = board ?? defaultId; | ||
| useEffect(() => { | ||
| if (board !== activeId && activeId !== null) { | ||
| setBoard(activeId); | ||
| } | ||
| }, [board, activeId, setBoard]); | ||
|
|
||
| return ( | ||
| <div className="mx-auto flex max-w-[1200px] flex-1 flex-col items-center gap-400 p-450"> | ||
| <CategorySelector items={items} activeId={activeId} onItemSelect={setBoard} /> | ||
| <div className="flex w-full flex-col items-start"> | ||
| <TitleInput /> | ||
| <div className="flex w-full items-center gap-200 rounded-lg p-100"> | ||
| <Editor /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <PostEditorShell | ||
| align="center" | ||
| header={<CategorySelector items={items} activeId={activeId} onItemSelect={setBoard} />} | ||
| /> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| 'use client'; | ||
|
|
||
| import { MoreVerticalIcon } from '@/assets/icons'; | ||
| import { | ||
| Button, | ||
| DropdownMenu, | ||
| DropdownMenuContent, | ||
| DropdownMenuItem, | ||
| DropdownMenuSeparator, | ||
| DropdownMenuTrigger, | ||
| Icon, | ||
| } from '@/components/ui'; | ||
| import { cn } from '@/lib/cn'; | ||
| import type { ButtonProps } from '@/components/ui'; | ||
|
|
||
| interface ActionMenuProps { | ||
| className?: string; | ||
| onEdit?: () => void; | ||
| onDeleteSelect?: (event: Event) => void; | ||
| triggerVariant?: ButtonProps['variant']; | ||
| triggerSize?: ButtonProps['size']; | ||
| triggerClassName?: string; | ||
| } | ||
|
|
||
| /** | ||
| * 수정/삭제 드롭다운 메뉴 | ||
| * | ||
| * 외부 핸들러에 동작을 위임합니다. post 삭제처럼 확인 다이얼로그 + API 호출이 | ||
| * 내장된 동작이 필요하면 `PostActionMenu`를 사용하세요. | ||
| */ | ||
| function ActionMenu({ | ||
| className, | ||
| onEdit, | ||
| onDeleteSelect, | ||
| triggerVariant = 'tertiary', | ||
| triggerSize = 'icon-md', | ||
| triggerClassName, | ||
| }: ActionMenuProps) { | ||
| return ( | ||
| <DropdownMenu modal> | ||
| <DropdownMenuTrigger asChild> | ||
| <Button | ||
| type="button" | ||
| variant={triggerVariant} | ||
| size={triggerSize} | ||
| className={cn('h-600 w-600', triggerClassName, className)} | ||
| aria-label="더보기" | ||
| onClickCapture={(e) => e.stopPropagation()} | ||
| > | ||
| <Icon src={MoreVerticalIcon} size={16} className="text-icon-normal" /> | ||
| </Button> | ||
| </DropdownMenuTrigger> | ||
| <DropdownMenuContent align="end" className="w-[144px]"> | ||
| <DropdownMenuItem onSelect={onEdit}>수정</DropdownMenuItem> | ||
| <DropdownMenuSeparator /> | ||
| <DropdownMenuItem destructive onSelect={onDeleteSelect}> | ||
| 삭제 | ||
| </DropdownMenuItem> | ||
| </DropdownMenuContent> | ||
| </DropdownMenu> | ||
| ); | ||
| } | ||
|
|
||
| export { ActionMenu, type ActionMenuProps }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
렌더중 store 변경은 권장되지 않으니까,, seEffect + ref guard 패턴이 더 좋을 거 같습니다!