[feature] 관리자 페이지 > 동아리 상세정보 수정 탭 추가 및 커버 업로드 UI 제공#958
Conversation
학기별로 수상 내역을 체계적으로 관리할 수 있는 에디터 구현 주요 기능: - CustomDropDown을 활용한 년도/학기 선택 UI (2020년~현재+1년) - 최신 학기순 자동 정렬 (2025 2학기 → 2025 1학기 → 2024 2학기...) - 학기별 최소 1개 수상 내역 보장 (조건부 삭제 버튼 렌더링) - 수상 내역/학기 추가 시 해당 입력란으로 자동 포커스 - 중복 학기 추가 방지 검증
자주 묻는 질문과 답변을 관리할 수 있는 독립 컴포넌트 구현 주요 기능: - FAQ 추가/삭제 기능 - 질문(100자), 답변(300자) 글자 수 제한 및 실시간 카운터 - FAQ 추가 시 질문 입력란으로 자동 포커스 이동 - 빈 상태일 때 안내 메시지 표시
동아리의 상세 정보를 통합 관리할 수 있는 새로운 어드민 탭 구현 관리 항목 - 📝 동아리 소개 (200자): "~를 소개할게요" 고정 포맷 - 🎯 이런 활동을 해요 (500자): 활동 내용 설명 - 🏆 이런 상을 받았어요: 학기별 수상 내역 (AwardEditor) - 💡 이런 사람이 오면 좋아요 (500자): 이상적인 인재상 - 🎁 부원이 가지는 혜택 (500자): 부원 혜택 안내 - ❓ FAQ: 자주 묻는 질문과 답변 (FAQEditor)
- font-weight: 700 → 600 - margin-bottom: 12px → 8px - color를 기본 색상으로 변경하여 InputField Label과 일관성 확보
동아리 상세 페이지에 표시될 커버 이미지를 관리하는 에디터 구현 주요 기능: - 커버 이미지 업로드 (10MB 이하, 375:213 비율 권장) - 기본 이미지로 초기화 기능 - ClubLogoEditor와 동일한 UI/UX 패턴 적용
ClubInfoEditTab에 ClubCoverEditor를 추가하여 커버 이미지 관리 기능 제공
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 사항 |
|---|---|
공통 스타일 조정 frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts |
Label margin-bottom 12px → 8px, 명시적 color 제거(상속 사용) |
라우팅 & 네비게이션 frontend/src/pages/AdminPage/AdminRoutes.tsx, frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx, frontend/src/App.tsx |
/admin/club-intro 라우트 추가; 사이드바 탭 구조 재편성(동아리 정보 항목 재구성), 테스트 라우트 /test/club-feed 추가 |
커버 편집 UI frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.ts, .../ClubCoverEditor.tsx |
클럽 커버 편집용 styled 컴포넌트 추가(여러 프레젠테이션 컴포넌트), 파일 선택/사이즈 검증, 업로드·리셋 흐름(업로드/삭제 구현 TODO, Mixpanel 이벤트 포함) |
ClubInfoEditTab 통합 변경 frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx |
<ClubCoverEditor /> 렌더링 추가, 헤더 타이틀 변경("동아리 카드 정보 수정" → "기본 정보 수정") |
타입 변경 frontend/src/types/club.ts |
Club 인터페이스에 선택적 cover?: string 필드 추가 |
ClubIntroTab 추가 frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.ts, .../ClubIntroTab.tsx |
ClubIntroTab 컴포넌트 및 Container 스타일 추가; 상세 설명, 활동, 장점, 수상, 이상적 지원자, FAQ 편집 상태 및 저장 로직(뮤테이션 호출) 도입 |
AwardEditor(스타일 + 컴포넌트) frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts, .../AwardEditor.tsx |
연도/학기 기반 수상 항목 추가·편집·삭제, 중복 방지, 정렬, 포커스 관리 등 동작 구현 및 관련 styled 컴포넌트 추가 |
FAQEditor(스타일 + 컴포넌트) frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts, .../FAQEditor.tsx |
FAQ 항목 추가·편집·삭제, 입력 포커스 관리, 글자수 표시 등 동작 구현 및 관련 styled 컴포넌트 추가 |
Sequence Diagram(s)
sequenceDiagram
actor Admin as 관리자
participant UI as ClubIntroTab UI
participant Sub as AwardEditor/FAQEditor
participant State as React State
participant Mutation as useUpdateClubDetail
participant API as Backend API
Admin->>UI: 저장 버튼 클릭
UI->>State: 로컬 유효성 검사 (clubDetail.id 확인)
UI->>Sub: onChange 통해 서브컴포넌트 상태 반영 (동기)
Sub-->>State: awards/faqs 등 업데이트 반영
State->>Mutation: 업데이트 데이터 전송
Mutation->>API: HTTP 요청 (PUT/PATCH)
alt 성공
API-->>Mutation: 200 OK
Mutation-->>State: 성공 응답
State->>UI: 캐시 무효화 / 성공 알림
UI-->>Admin: "저장되었습니다" 알림
else 실패
API-->>Mutation: Error
Mutation-->>State: 에러 전달
State->>UI: 에러 알림
UI-->>Admin: 에러 표시
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related issues
- MOA-443: 관리자페이지 동아리 상세정보 수정 탭 추가 — 본 PR은 ClubIntroTab 및 관련 서브컴포넌트 추가와 라우팅 변경을 통해 해당 요구사항을 구현합니다.
- [feature] MOA-443 관리자페이지 > 동아리 상세정보 수정 탭 추가 #949 (참조된 이슈) — 동일 목표(상세정보 제작 탭 추가)로 연관성이 높음.
Possibly related PRs
- [feature] 동아리 커버 이미지 업로드 및 삭제 hook 추가 #957 — 커버 업로드/삭제 훅 구현 PR과 기능적으로 연결(ClubCoverEditor의 업로드/삭제 TODO 해결 가능).
- [release] FE v1.1.1 #733 —
CustomTextArea.styles.ts의 Label 스타일 변경과 중복 변경 가능성 존재. - [feature] 관리자페이지 사이드바에 지원서 제작 탭 추가 #489 — 관리자 사이드바/라우팅 변경과 코드 레벨 연관성 있음.
Suggested reviewers
- seongwon030
- lepitaaar
- suhyun113
- seongje973
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목이 변경 사항의 주요 내용을 명확하게 설명합니다. 관리자 페이지에 동아리 상세정보 수정 탭 추가 및 커버 업로드 UI 제공이라는 핵심 변경사항을 정확히 요약했습니다. |
| Linked Issues check | ✅ Passed | PR이 MOA-443의 요구사항을 모두 충족합니다. 상세정보 제작 탭 추가, 기존 탭 네비게이션과 동일한 구조 유지, 탭 클릭 시 정상 진입 및 상세정보 편집 UI(FAQ, 수상 내역, 커버 이미지)를 완전히 구현했습니다. |
| Out of Scope Changes check | ✅ Passed | CustomTextArea의 Label 스타일 수정과 테스트 페이지(/test/club-feed) 추가는 주요 목표 범위 내의 관련 개선 사항이거나 작은 보조 변경입니다. 주요 범위를 벗어난 변경은 없습니다. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#949-admin-club-detail-editor-tab-MOA-443
📜 Recent review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
- frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (5)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsx (1)
42-51: 포커스 관리 로직 개선 제안현재 구현은 동작하지만, shouldFocusLast 상태와 useEffect를 사용하는 방식은 추가적인 렌더링을 발생시킵니다. faqs 배열의 길이 변화를 직접 감지하는 방식으로 단순화할 수 있습니다.
🔎 개선된 포커스 관리 패턴
- const [shouldFocusLast, setShouldFocusLast] = useState(false); + const prevFaqsLengthRef = useRef(faqs.length); const questionInputRefs = useRef<(HTMLInputElement | null)[]>([]); const handleAddFAQ = () => { const newFAQ: FAQ = { question: '', answer: '', }; onChange([...faqs, newFAQ]); - setShouldFocusLast(true); }; useEffect(() => { - if (shouldFocusLast && faqs.length > 0) { + if (faqs.length > prevFaqsLengthRef.current) { const lastIndex = faqs.length - 1; const inputRef = questionInputRefs.current[lastIndex]; if (inputRef) { inputRef.focus(); } - setShouldFocusLast(false); } + prevFaqsLengthRef.current = faqs.length; - }, [faqs, shouldFocusLast]); + }, [faqs.length]);frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (1)
27-27: 불필요한 빈 줄 제거"모집 정보" 카테고리의 items 배열 내 빈 줄을 제거하여 코드를 정리하세요.
🔎 제안된 수정
items: [ { label: '모집 정보 수정', path: '/admin/recruit-edit' }, - ],frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx (1)
2-2: 커버 이미지 기본값으로 프로필 이미지 사용
default_profile_image.svg를 커버 이미지의 기본값으로 사용하고 있습니다. 프로필 이미지와 커버 이미지는 비율(375:213)과 용도가 다르므로 별도의 기본 커버 이미지를 사용하는 것이 좋습니다.frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.ts (1)
43-77: 버튼 스타일 중복
UploadButton과ResetButton이 색상을 제외하고 대부분의 스타일을 공유하고 있습니다. 공통 스타일을 베이스로 추출하여 중복을 줄이는 것을 고려해보세요.🔎 리팩토링 제안
const BaseButton = styled.button` padding: 10px 20px; border-radius: 80px; background: ${({ theme }) => theme.colors.base.white}; font-size: 12px; line-height: 140%; font-weight: 600; cursor: pointer; transition: all 0.2s; `; export const UploadButton = styled(BaseButton)` border: 1px solid ${({ theme }) => theme.colors.primary[900]}; color: ${({ theme }) => theme.colors.primary[900]}; &:hover { background: ${({ theme }) => theme.colors.primary[900]}; color: ${({ theme }) => theme.colors.base.white}; } `; export const ResetButton = styled(BaseButton)` border: 1px solid ${({ theme }) => theme.colors.gray[600]}; color: ${({ theme }) => theme.colors.gray[600]}; &:hover { background: ${({ theme }) => theme.colors.gray[600]}; color: ${({ theme }) => theme.colors.base.white}; } `;frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsx (1)
52-55: 렌더링마다 정렬 수행으로 인한 성능 저하
sortedAwards가 매 렌더링마다 계산되고 있습니다.awards배열이 변경되지 않은 경우에도 불필요하게 정렬이 수행됩니다.useMemo를 사용하여awards가 변경될 때만 정렬하도록 최적화하세요.🔎 수정 제안
+import { useEffect, useMemo, useRef, useState } from 'react'; - const sortedAwards = [...awards].sort( - (awardA, awardB) => - parseSemester(awardB.semester) - parseSemester(awardA.semester), - ); + const sortedAwards = useMemo( + () => + [...awards].sort( + (awardA, awardB) => + parseSemester(awardB.semester) - parseSemester(awardA.semester), + ), + [awards], + );
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
frontend/src/components/common/CustomTextArea/CustomTextArea.styles.tsfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/types/club.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.styles.tsfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsxfrontend/src/types/club.tsfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.styles.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.styles.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.styles.tsfrontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsxfrontend/src/types/club.tsfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.styles.tsfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.styles.ts
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsx
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs
Applied to files:
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.ts
🧬 Code graph analysis (5)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.ts (1)
frontend/src/styles/theme/index.ts (1)
theme(4-7)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.tsx (1)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsx (1)
FAQ(21-24)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsx (1)
frontend/src/components/common/CustomDropDown/CustomDropDown.tsx (1)
CustomDropDown(94-115)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.ts (1)
frontend/src/styles/theme/index.ts (1)
theme(4-7)
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.styles.ts (1)
frontend/src/styles/theme/index.ts (1)
theme(4-7)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx
[error] 19-19: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (10)
frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts (1)
14-14: LGTM!Label의 margin-bottom을 8px로 조정하여 InputField와 일관된 스타일을 유지하는 변경사항입니다.
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/FAQEditor/FAQEditor.styles.ts (1)
1-128: LGTM!스타일 컴포넌트들이 일관된 테마 토큰을 사용하고 있으며, hover/focus 상태가 적절히 구현되어 있습니다.
frontend/src/types/club.ts (1)
7-7: LGTM!Club 인터페이스에 선택적 cover 필드를 추가하여 커버 이미지 URL을 관리할 수 있게 되었습니다. 선택적 필드로 정의하여 기존 코드와의 호환성을 유지합니다.
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (1)
12-12: LGTM!ClubCoverEditor를 통합하고 헤더 제목을 "기본 정보 수정"으로 변경하여 새로운 관리자 페이지 구조와 일관성을 유지합니다. 커버 이미지 prop이 올바르게 전달되고 있습니다.
Also applies to: 150-150, 160-160
frontend/src/pages/AdminPage/AdminRoutes.tsx (1)
12-12: LGTM!ClubDetailTab 라우트가 올바르게 추가되었습니다. 라우트 경로('/admin/club-detail')가 사이드바 설정과 일치합니다.
Also applies to: 38-38
frontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.styles.ts (1)
1-7: LGTM!ClubDetailTab의 레이아웃 컨테이너가 간결하게 정의되어 있습니다. 60px gap은 섹션 간 적절한 여백을 제공합니다.
frontend/src/pages/AdminPage/tabs/ClubDetailTab/ClubDetailTab.tsx (2)
50-60: 상태 초기화 로직 검토 필요
clubDetail이 변경될 때마다 모든 편집 상태를 빈 값으로 초기화하고 있습니다. 이는 사용자가 입력한 내용을 잃게 만들 수 있습니다. API 연동 후에는clubDetail의 실제 데이터로 상태를 초기화하거나, 사용자의 미저장 변경사항이 있을 경우 경고를 표시하는 로직이 필요합니다.PR 노트에서 언급된 "unsaved-changes warning or data retention when switching tabs" 기능이 이 문제를 해결할 것으로 보입니다.
41-44: IdealCandidate의 tags 필드 미사용
IdealCandidate인터페이스에tags필드가 정의되어 있지만, UI에서는content만 편집하고tags는 사용되지 않습니다. API 스펙에tags가 필요한 경우 UI를 추가하거나, 불필요한 경우 인터페이스에서 제거하는 것이 좋습니다.Also applies to: 127-136
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.tsx (1)
1-266: 코드 구현 품질 우수Award 에디터의 전반적인 구현이 견고합니다:
- 중복 학기 방지 로직 (lines 60-64)
- 자동 포커스 관리 (lines 124-139)
- 불변성을 유지하는 상태 업데이트
- 최신 학기 우선 정렬
사용자 경험과 데이터 무결성을 모두 고려한 구현입니다.
frontend/src/pages/AdminPage/tabs/ClubDetailTab/components/AwardEditor/AwardEditor.styles.ts (1)
1-182: 스타일 정의 양호AwardEditor의 모든 스타일 컴포넌트가 테마를 일관되게 사용하고 있으며, 적절한 여백, 레이아웃, 그리고 인터랙티브 상태를 제공합니다.
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.styles.ts
Show resolved
Hide resolved
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx
Outdated
Show resolved
Hide resolved
frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx
Show resolved
Hide resolved
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
Show resolved
Hide resolved
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
Outdated
Show resolved
Hide resolved
- FAQ 인터페이스에 고유 id 필드 추가 - React key를 배열 index에서 고유 id로 변경하여 재조정 최적화
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx (1)
64-72: 배열 인덱스를 key로 사용하는 것에 대한 기존 이슈이 부분은 이전 리뷰에서 지적된 바와 같이, FAQ 항목 삭제나 순서 변경 시 React의 재조정 알고리즘에 문제가 발생할 수 있습니다. FAQ 인터페이스에 고유
id필드를 추가하고 이를 key로 사용하는 것을 권장합니다.frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
80-81:as any타입 단언으로 인한 타입 안전성 우회이 부분은 이전 리뷰에서 지적된 대로,
updatedData가ClubDetail타입에 없는 필드들을 포함하고 있어as any로 우회하고 있습니다. API 연동 시 적절한 타입 정의가 필요합니다. TODO 주석이 있으므로 향후 API 연동 시 해결될 것으로 보입니다.
🧹 Nitpick comments (4)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsx (1)
224-255: Achievement 항목에 배열 인덱스를 key로 사용
achievementIndex를 key로 사용하면 수상 내역 삭제 시 React 재조정에 문제가 발생할 수 있습니다. 수상 내역이 문자열 배열이므로, 고유 ID를 추가하거나 삭제 시에만 발생하는 버그라면 현재 구현도 수용 가능합니다.🔎 고유 ID 기반 구조로 변경 제안
Achievement를 문자열 대신 객체로 관리하면 더 안정적입니다:
interface Achievement { id: string; content: string; }또는 현재 구조를 유지하면서 복합 키를 사용할 수 있습니다:
- <Styled.AchievementItem key={achievementIndex}> + <Styled.AchievementItem key={`${award.semester}-${achievementIndex}-${achievement}`}>frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
33-33: 페이지 뷰 트래킹 상수 확인 필요
PAGE_VIEW.CLUB_INFO_EDIT_PAGE는 기본 정보 수정 페이지용 상수입니다. ClubIntroTab은 새로운 상세 정보 수정 탭이므로 별도의 페이지 뷰 상수(예:CLUB_INTRO_EDIT_PAGE)를 추가하는 것이 분석 데이터 구분에 도움이 됩니다.🔎 새로운 페이지 뷰 상수 추가 제안
frontend/src/constants/eventName.ts에 새로운 상수 추가:export const PAGE_VIEW = { // 관리자 LOGIN_PAGE: '로그인페이지', CLUB_INFO_EDIT_PAGE: '동아리 기본 정보 수정 페이지', + CLUB_INTRO_EDIT_PAGE: '동아리 상세 정보 수정 페이지', RECRUITMENT_INFO_EDIT_PAGE: '동아리 모집 정보 수정 페이지',그리고 ClubIntroTab.tsx에서 사용:
- useTrackPageView(PAGE_VIEW.CLUB_INFO_EDIT_PAGE); + useTrackPageView(PAGE_VIEW.CLUB_INTRO_EDIT_PAGE);frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts (1)
3-36: AwardEditor.styles.ts와 스타일 중복이 있습니다.
Container,Label,AddButton,RemoveButton등이 AwardEditor.styles.ts와 거의 동일합니다. 현재는 각 에디터별로 분리되어 있어 유지보수에 문제가 없지만, 향후 재사용 가능한 공통 스타일 컴포넌트로 추출하면 일관성 유지에 도움이 될 수 있습니다.Also applies to: 75-92
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts (1)
21-32: 사용되지 않는 Input 스타일 컴포넌트 제거 고려
Input스타일 컴포넌트(21-32줄)가AwardEditor.tsx에서 사용되지 않습니다. 년도/학기 선택은DropdownTrigger를, 수상 내용 입력은AchievementInput을 사용합니다. 제거하기를 권장합니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
frontend/src/App.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsxfrontend/src/pages/AdminPage/components/SideBar/SideBar.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.tsfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/pages/AdminPage/components/ClubCoverEditor/ClubCoverEditor.tsx
- frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.tsfrontend/src/App.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.tsfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.tsfrontend/src/App.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.tsfrontend/src/pages/AdminPage/AdminRoutes.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/App.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsx
🧠 Learnings (9)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/App.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsxfrontend/src/pages/AdminPage/AdminRoutes.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsxfrontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions/hooks
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts
🧬 Code graph analysis (6)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.ts (2)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts (1)
Container(3-7)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx (1)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
FAQ(21-24)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsx (2)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (1)
Award(16-19)frontend/src/components/common/CustomDropDown/CustomDropDown.tsx (1)
CustomDropDown(94-115)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (4)
frontend/src/constants/eventName.ts (2)
PAGE_VIEW(79-93)ADMIN_EVENT(37-77)frontend/src/types/club.ts (1)
ClubDetail(17-30)frontend/src/hooks/queries/club/useUpdateClubDetail.ts (1)
useUpdateClubDetail(5-14)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/components/SelectTags/SelectTags.styles.ts (1)
Button(8-25)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts (2)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts (4)
Container(3-7)Label(9-13)AddButton(56-70)RemoveButton(99-116)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts (3)
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.ts (1)
Container(3-7)frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts (4)
Container(3-7)Label(15-19)AddButton(21-36)RemoveButton(75-92)frontend/src/styles/theme/index.ts (1)
theme(4-7)
🔇 Additional comments (9)
frontend/src/pages/AdminPage/AdminRoutes.tsx (1)
12-12: LGTM!새로운 라우트와 import가 기존 패턴을 잘 따르고 있습니다.
club-intro경로가 다른 탭들과 일관된 구조로 추가되었습니다.Also applies to: 38-38
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.styles.ts (1)
1-7: LGTM!최상위 컨테이너 스타일이 간결하게 정의되어 있습니다. 60px gap은 주요 섹션 간 적절한 간격을 제공합니다.
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.tsx (1)
11-51: FAQ 에디터 로직 구현이 잘 되어 있습니다.불변 상태 업데이트 패턴과 새로 추가된 FAQ 항목에 대한 자동 포커스 기능이 적절하게 구현되었습니다.
questionInputRefs배열을 통한 ref 관리와shouldFocusLast플래그를 활용한 포커스 로직이 깔끔합니다.frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.tsx (2)
57-73: 중복 학기 체크 로직이 잘 구현되어 있습니다.
handleAddSemester에서 중복 학기를 검사하고 사용자에게 알림을 표시하는 방어적 코딩이 적절합니다. 모든 핸들러가 불변 업데이트 패턴을 올바르게 사용하고 있습니다.
146-206: 드롭다운 구현이 CustomDropDown 컴포넌트와 잘 통합되어 있습니다.연도와 학기 선택을 위한 CustomDropDown 사용이 적절하며, 일관된 스타일링이 적용되어 있습니다.
frontend/src/pages/AdminPage/tabs/ClubIntroTab/ClubIntroTab.tsx (2)
50-60: 데이터 초기화 로직의 현재 상태 확인TODO 주석에 표시된 대로 API 연동 시 실제 데이터로 대체되어야 합니다. 현재는 모든 필드가 빈 값으로 초기화되어 기존 데이터를 불러올 수 없습니다. API 연동 작업 시
clubDetail에서 해당 필드들을 읽어 초기화하도록 수정이 필요합니다.
94-150: 컴포넌트 구성이 잘 되어 있습니다.ContentSection을 활용한 레이아웃과 CustomTextArea, AwardEditor, FAQEditor의 조합이 일관된 UI를 제공합니다. 각 필드에 적절한
maxLength제한과 플레이스홀더가 설정되어 있어 사용자 경험이 좋습니다.frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/FAQEditor/FAQEditor.styles.ts (1)
94-128: 입력 필드 스타일이 잘 정의되어 있습니다.QuestionInput과 AnswerTextArea의 포커스 상태, AnswerTextArea의 vertical resize 허용, CharCount의 간결한 스타일링이 좋은 UX를 제공합니다.
frontend/src/pages/AdminPage/tabs/ClubIntroTab/components/AwardEditor/AwardEditor.styles.ts (1)
145-182: Achievement 관련 버튼 스타일이 잘 구현되어 있습니다.
AchievementRemoveButton의 절대 위치 지정과 호버 시 opacity 전환,AddAchievementButton의 dashed border 스타일이 직관적인 UI를 제공합니다.
#️⃣ 연관된 이슈
📝 작업 요약
이번 PR에서는 관리자 페이지에 동아리 상세 정보 관리 기능을 추가했습니다.
동아리의 기본 정보, FAQ, 수상 내역, 커버 이미지를 관리할 수 있는 에디터 컴포넌트를 구현했습니다.
🛠 작업 내용
1. 동아리 상세 정보 관리 기능
관리 탭 추가 (/admin/club-detail)
FAQ 관리 에디터
수상 내역 관리 에디터
2. 커버 이미지 관리 기능
커버 이미지 에디터 구현
기본정보 탭 통합
3. 관리자 페이지 구조 개선
라우팅 구조 개선
SideBar 정렬 개선
4. UI 일관성 및 리팩토링
CustomTextArea 스타일 통일
컴포넌트 구조 정리
💬 논의하고 싶은 부분
🤖 AI 활용한 부분
🫡 참고 사항
Summary by CodeRabbit
릴리스 노트
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.