diff --git a/docs/frontend-improvement.md b/docs/frontend-improvement.md index 364238a..11c07d7 100644 --- a/docs/frontend-improvement.md +++ b/docs/frontend-improvement.md @@ -48,7 +48,7 @@ components/ | Schedules.jsx | 1465 | 1159 | ✅ 분리 완료 | | ScheduleForm.jsx | 1047 | 765 | ✅ 분리 완료 | | ScheduleDict.jsx | 714 | 572 | ✅ 분리 완료 | -| AlbumForm.jsx | 631 | 631 | 미분리 | +| AlbumForm.jsx | 631 | 443 | ✅ 분리 완료 | --- @@ -96,8 +96,10 @@ components/ │ │ ├── ScheduleItem.jsx # 일정 아이템 (리스트용) │ │ ├── LocationSearchDialog.jsx # 장소 검색 모달 │ │ ├── MemberSelector.jsx # 멤버 선택 UI -│ │ └── ImageUploader.jsx # 이미지 업로드 -│ └── album/ # PhotoUploader 등 +│ │ ├── ImageUploader.jsx # 이미지 업로드 +│ │ └── WordItem.jsx # 사전 단어 아이템 +│ └── album/ # ✅ 추가된 컴포넌트들: +│ └── TrackItem.jsx # 트랙 입력 폼 ``` ### 2.3 중복 코드 제거 @@ -179,8 +181,10 @@ pages/pc/admin/schedules/ 3. [x] ScheduleDict.jsx 분리 (714줄 → 572줄, 142줄 감소) - `WordItem.jsx` 추출 (단어 테이블 행 컴포넌트) - `POS_TAGS` 상수 함께 분리 -4. [ ] AlbumPhotos.jsx 분리 (1536줄, 구조 복잡) -5. [ ] AlbumForm.jsx 분리 (631줄) +4. [x] AlbumForm.jsx 분리 (631줄 → 443줄, 188줄 감소) + - `CustomSelect.jsx` 추출 (공통 드롭다운 → common/) + - `TrackItem.jsx` 추출 (트랙 입력 폼 → album/) +5. [ ] AlbumPhotos.jsx 분리 (1536줄, 구조 복잡) ### Phase 3: 추가 개선 1. [x] 관리자 페이지용 에러 페이지 추가 (404) diff --git a/frontend-temp/src/components/pc/admin/album/TrackItem.jsx b/frontend-temp/src/components/pc/admin/album/TrackItem.jsx new file mode 100644 index 0000000..7e4c2a1 --- /dev/null +++ b/frontend-temp/src/components/pc/admin/album/TrackItem.jsx @@ -0,0 +1,153 @@ +/** + * 앨범 트랙 입력 컴포넌트 + */ +import { memo } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Trash2, Star, ChevronDown } from 'lucide-react'; + +/** + * @param {Object} props + * @param {Object} props.track - 트랙 데이터 + * @param {number} props.index - 트랙 인덱스 + * @param {Function} props.onUpdate - 트랙 업데이트 핸들러 (index, field, value) + * @param {Function} props.onRemove - 트랙 삭제 핸들러 () + */ +const TrackItem = memo(function TrackItem({ track, index, onUpdate, onRemove }) { + return ( +
+
+
+ + {String(track.track_number).padStart(2, '0')} + + +
+ +
+ +
+
+ onUpdate(index, 'title', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="곡 제목" + /> +
+
+ onUpdate(index, 'duration', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-center" + placeholder="0:00" + /> +
+
+ + {/* 상세 정보 토글 */} +
+ +
+ + + {track.showDetails && ( + + {/* 작사/작곡/편곡 */} +
+
+ + onUpdate(index, 'lyricist', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="여러 명일 경우 쉼표로 구분" + /> +
+
+ + onUpdate(index, 'composer', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="여러 명일 경우 쉼표로 구분" + /> +
+
+ + onUpdate(index, 'arranger', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="여러 명일 경우 쉼표로 구분" + /> +
+
+ + {/* MV URL */} +
+ + onUpdate(index, 'music_video_url', e.target.value)} + className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" + placeholder="https://youtube.com/watch?v=..." + /> +
+ + {/* 가사 */} +
+ +