From 2d42bf1603ba9009467b36e13b1e232d69460021 Mon Sep 17 00:00:00 2001 From: caadiq Date: Wed, 21 Jan 2026 20:11:21 +0900 Subject: [PATCH] =?UTF-8?q?revert(frontend):=20Phase=205=EB=A1=9C=20?= =?UTF-8?q?=EB=A1=A4=EB=B0=B1=20-=20=EA=B5=AC=EC=A1=B0=20=EC=9E=AC?= =?UTF-8?q?=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 6-9에서 추가된 파일 제거 - react-device-detect 미사용 문제로 인한 구조 재설계 - 올바른 폴더 구조로 재시작 예정: - pages/{feature}/pc/, pages/{feature}/mobile/ 구조 - react-device-detect BrowserView/MobileView 사용 - components/pc/, components/mobile/ 레이아웃 분리 Co-Authored-By: Claude Opus 4.5 --- frontend-temp/src/App.jsx | 187 ++++++---- .../src/components/common/ErrorBoundary.jsx | 62 ---- .../src/components/common/Lightbox.jsx | 225 ------------ .../src/components/common/Loading.jsx | 53 --- frontend-temp/src/components/common/Toast.jsx | 103 ------ frontend-temp/src/components/common/index.js | 7 - frontend-temp/src/components/index.js | 12 - .../src/components/layout/Footer.jsx | 18 - .../src/components/layout/Header.jsx | 77 ---- .../src/components/layout/Layout.jsx | 104 ------ .../src/components/layout/MobileNav.jsx | 44 --- frontend-temp/src/components/layout/index.js | 7 - .../src/components/schedule/ScheduleCard.jsx | 233 ------------ .../src/components/schedule/index.js | 4 - frontend-temp/src/constants/index.js | 8 - frontend-temp/src/hooks/index.js | 6 - frontend-temp/src/hooks/useCalendar.js | 44 +-- frontend-temp/src/pages/album/Album.jsx | 188 ---------- frontend-temp/src/pages/album/index.js | 4 - frontend-temp/src/pages/common/NotFound.jsx | 30 -- frontend-temp/src/pages/common/index.js | 1 - frontend-temp/src/pages/home/Home.jsx | 333 ------------------ frontend-temp/src/pages/home/index.js | 1 - frontend-temp/src/pages/index.js | 8 - frontend-temp/src/pages/members/Members.jsx | 181 ---------- frontend-temp/src/pages/members/index.js | 1 - frontend-temp/src/pages/schedule/Schedule.jsx | 330 ----------------- frontend-temp/src/pages/schedule/index.js | 4 - 28 files changed, 130 insertions(+), 2145 deletions(-) delete mode 100644 frontend-temp/src/components/common/ErrorBoundary.jsx delete mode 100644 frontend-temp/src/components/common/Lightbox.jsx delete mode 100644 frontend-temp/src/components/common/Loading.jsx delete mode 100644 frontend-temp/src/components/common/Toast.jsx delete mode 100644 frontend-temp/src/components/common/index.js delete mode 100644 frontend-temp/src/components/index.js delete mode 100644 frontend-temp/src/components/layout/Footer.jsx delete mode 100644 frontend-temp/src/components/layout/Header.jsx delete mode 100644 frontend-temp/src/components/layout/Layout.jsx delete mode 100644 frontend-temp/src/components/layout/MobileNav.jsx delete mode 100644 frontend-temp/src/components/layout/index.js delete mode 100644 frontend-temp/src/components/schedule/ScheduleCard.jsx delete mode 100644 frontend-temp/src/components/schedule/index.js delete mode 100644 frontend-temp/src/pages/album/Album.jsx delete mode 100644 frontend-temp/src/pages/album/index.js delete mode 100644 frontend-temp/src/pages/common/NotFound.jsx delete mode 100644 frontend-temp/src/pages/common/index.js delete mode 100644 frontend-temp/src/pages/home/Home.jsx delete mode 100644 frontend-temp/src/pages/home/index.js delete mode 100644 frontend-temp/src/pages/index.js delete mode 100644 frontend-temp/src/pages/members/Members.jsx delete mode 100644 frontend-temp/src/pages/members/index.js delete mode 100644 frontend-temp/src/pages/schedule/Schedule.jsx delete mode 100644 frontend-temp/src/pages/schedule/index.js diff --git a/frontend-temp/src/App.jsx b/frontend-temp/src/App.jsx index c256c2e..767b4a6 100644 --- a/frontend-temp/src/App.jsx +++ b/frontend-temp/src/App.jsx @@ -1,75 +1,144 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; -import { ErrorBoundary, ToastContainer, Layout } from "@/components"; -import { Schedule, Album, Home, Members, NotFound } from "@/pages"; +import { cn, getTodayKST, formatFullDate } from "@/utils"; +import { useAuthStore, useUIStore } from "@/stores"; +import { useIsMobile, useCategories, useCalendar } from "@/hooks"; +import { memberApi } from "@/api"; +import { useQuery } from "@tanstack/react-query"; /** * 프로미스나인 팬사이트 메인 앱 * - * Phase 9: 기타 공개 페이지 마이그레이션 - * - Home 페이지 (PC/Mobile 통합) - * - Members 페이지 (PC/Mobile 통합) - * - NotFound 페이지 + * Phase 5: 커스텀 훅 완료 + * - useMediaQuery, useIsMobile, useIsDesktop + * - useScheduleData, useScheduleDetail, useCategories + * - useScheduleSearch (무한 스크롤) + * - useScheduleFiltering, useCategoryCounts + * - useCalendar + * - useAdminAuth */ function App() { + const today = getTodayKST(); + const isMobile = useIsMobile(); + const { isAuthenticated } = useAuthStore(); + const { showSuccess, toasts } = useUIStore(); + + // 커스텀 훅 사용 + const { data: categories, isLoading: categoriesLoading } = useCategories(); + const calendar = useCalendar(); + + // 멤버 데이터 (기존 방식 유지) + const { data: members, isLoading: membersLoading } = useQuery({ + queryKey: ["members"], + queryFn: memberApi.getMembers, + }); + return ( - - - {/* 홈 */} - - - - - } - /> + + +
+

+ fromis_9 Frontend Refactoring +

+

Phase 5 완료 - 커스텀 훅

+

+ 디바이스: {isMobile ? "모바일" : "PC"} (useIsMobile 훅) +

- {/* 멤버 */} - - - - - } - /> +
+

오늘: {formatFullDate(today)}

+

인증: {isAuthenticated ? "✅" : "❌"}

- {/* 앨범 */} - - - - - } - /> +
+

useCategories 훅

+

+ {categoriesLoading ? "로딩 중..." : `${categories?.length || 0}개 카테고리`} +

+ {categories && ( +
+ {categories.map((c) => ( + + {c.name} + + ))} +
+ )} +
- {/* 스케줄 */} - - - - - } - /> +
+

useCalendar 훅

+

현재: {calendar.year}년 {calendar.monthName}

+

선택: {calendar.selectedDate}

+
+ + + +
+
- {/* 404 */} - - - - } - /> - - +
+

멤버 데이터

+

{membersLoading ? "로딩 중..." : `${members?.length || 0}명`}

+ {members && ( +
+ {members.map((m) => ( + + {m.name} + + ))} +
+ )} +
+
+
+ + {/* 토스트 표시 */} +
+ {toasts.map((toast) => ( +
+ {toast.message} +
+ ))} +
+ + } + /> +
); } diff --git a/frontend-temp/src/components/common/ErrorBoundary.jsx b/frontend-temp/src/components/common/ErrorBoundary.jsx deleted file mode 100644 index a4aff59..0000000 --- a/frontend-temp/src/components/common/ErrorBoundary.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Component } from 'react'; -import { AlertTriangle, RefreshCw } from 'lucide-react'; - -/** - * 에러 경계 컴포넌트 - * React 컴포넌트 트리에서 발생하는 에러를 캐치하여 폴백 UI를 표시 - */ -class ErrorBoundary extends Component { - state = { hasError: false, error: null }; - - static getDerivedStateFromError(error) { - return { hasError: true, error }; - } - - componentDidCatch(error, errorInfo) { - console.error('ErrorBoundary:', error, errorInfo); - } - - handleReset = () => { - this.setState({ hasError: false, error: null }); - }; - - render() { - if (this.state.hasError) { - // 커스텀 폴백이 있으면 사용 - if (this.props.fallback) { - return this.props.fallback; - } - - return ( -
- -

- 문제가 발생했습니다 -

-

- {this.state.error?.message || '알 수 없는 오류'} -

-
- - -
-
- ); - } - - return this.props.children; - } -} - -export default ErrorBoundary; diff --git a/frontend-temp/src/components/common/Lightbox.jsx b/frontend-temp/src/components/common/Lightbox.jsx deleted file mode 100644 index 4fa2a20..0000000 --- a/frontend-temp/src/components/common/Lightbox.jsx +++ /dev/null @@ -1,225 +0,0 @@ -import { useState, useEffect, useCallback, memo } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { X, ChevronLeft, ChevronRight } from 'lucide-react'; -import { useUIStore } from '@/stores'; - -/** - * 라이트박스 인디케이터 - */ -const LightboxIndicator = memo(function LightboxIndicator({ - count, - currentIndex, - goToIndex, -}) { - const translateX = -(currentIndex * 18) + 100 - 6; - - return ( -
- {/* 양옆 페이드 그라데이션 */} -
- {/* 슬라이딩 컨테이너 */} -
- {Array.from({ length: count }).map((_, i) => ( -
-
- ); -}); - -/** - * 라이트박스 컴포넌트 - * useUIStore와 연동하거나 props로 직접 제어 가능 - */ -function Lightbox({ images: propImages, currentIndex: propIndex, isOpen: propIsOpen, onClose: propOnClose, onIndexChange: propOnIndexChange }) { - // useUIStore에서 상태 가져오기 (props가 없으면 스토어 사용) - const store = useUIStore(); - - const images = propImages ?? store.lightboxImages; - const currentIndex = propIndex ?? store.lightboxIndex; - const isOpen = propIsOpen ?? store.lightboxOpen; - const onClose = propOnClose ?? store.closeLightbox; - const onIndexChange = propOnIndexChange ?? store.setLightboxIndex; - - const [imageLoaded, setImageLoaded] = useState(false); - const [slideDirection, setSlideDirection] = useState(0); - - // 이전/다음 네비게이션 - const goToPrev = useCallback(() => { - if (images.length <= 1) return; - setImageLoaded(false); - setSlideDirection(-1); - onIndexChange((currentIndex - 1 + images.length) % images.length); - }, [images.length, currentIndex, onIndexChange]); - - const goToNext = useCallback(() => { - if (images.length <= 1) return; - setImageLoaded(false); - setSlideDirection(1); - onIndexChange((currentIndex + 1) % images.length); - }, [images.length, currentIndex, onIndexChange]); - - const goToIndex = useCallback( - (index) => { - if (index === currentIndex) return; - setImageLoaded(false); - setSlideDirection(index > currentIndex ? 1 : -1); - onIndexChange(index); - }, - [currentIndex, onIndexChange] - ); - - // 라이트박스 열릴 때 body 스크롤 숨기기 - useEffect(() => { - if (isOpen) { - document.documentElement.style.overflow = 'hidden'; - document.body.style.overflow = 'hidden'; - } else { - document.documentElement.style.overflow = ''; - document.body.style.overflow = ''; - } - return () => { - document.documentElement.style.overflow = ''; - document.body.style.overflow = ''; - }; - }, [isOpen]); - - // 키보드 이벤트 핸들러 - useEffect(() => { - if (!isOpen) return; - - const handleKeyDown = (e) => { - switch (e.key) { - case 'ArrowLeft': - goToPrev(); - break; - case 'ArrowRight': - goToNext(); - break; - case 'Escape': - onClose(); - break; - default: - break; - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [isOpen, goToPrev, goToNext, onClose]); - - // 이미지가 바뀔 때 로딩 상태 리셋 - useEffect(() => { - setImageLoaded(false); - }, [currentIndex]); - - return ( - - {isOpen && images.length > 0 && ( - -
- {/* 닫기 버튼 */} - - - {/* 이전 버튼 */} - {images.length > 1 && ( - - )} - - {/* 로딩 스피너 */} - {!imageLoaded && ( -
-
-
- )} - - {/* 이미지 */} -
- e.stopPropagation()} - onLoad={() => setImageLoaded(true)} - initial={{ x: slideDirection * 100 }} - animate={{ x: 0 }} - transition={{ duration: 0.25, ease: 'easeOut' }} - /> -
- - {/* 다음 버튼 */} - {images.length > 1 && ( - - )} - - {/* 인디케이터 */} - {images.length > 1 && ( - - )} -
- - )} - - ); -} - -export default Lightbox; diff --git a/frontend-temp/src/components/common/Loading.jsx b/frontend-temp/src/components/common/Loading.jsx deleted file mode 100644 index ce23d97..0000000 --- a/frontend-temp/src/components/common/Loading.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import { cn } from '@/utils'; - -/** - * 로딩 스피너 컴포넌트 - * @param {object} props - * @param {'sm'|'md'|'lg'} props.size - 크기 - * @param {string} props.className - 추가 클래스 - * @param {string} props.text - 로딩 텍스트 - */ -export default function Loading({ size = 'md', className = '', text = '' }) { - const sizeClasses = { - sm: 'h-6 w-6 border-2', - md: 'h-10 w-10 border-3', - lg: 'h-12 w-12 border-4', - }; - - return ( -
-
- {text &&

{text}

} -
- ); -} - -/** - * 전체 화면 로딩 - */ -export function FullPageLoading({ text = '로딩 중...' }) { - return ( -
- -
- ); -} - -/** - * 인라인 로딩 (버튼 등에 사용) - */ -export function InlineLoading({ className = '' }) { - return ( -
- ); -} diff --git a/frontend-temp/src/components/common/Toast.jsx b/frontend-temp/src/components/common/Toast.jsx deleted file mode 100644 index 33556b2..0000000 --- a/frontend-temp/src/components/common/Toast.jsx +++ /dev/null @@ -1,103 +0,0 @@ -import { motion, AnimatePresence } from 'framer-motion'; -import { X, CheckCircle, AlertCircle, AlertTriangle, Info } from 'lucide-react'; -import { cn } from '@/utils'; -import { useUIStore } from '@/stores'; - -/** - * 토스트 아이콘 매핑 - */ -const icons = { - success: CheckCircle, - error: AlertCircle, - warning: AlertTriangle, - info: Info, -}; - -/** - * 토스트 스타일 매핑 - */ -const styles = { - success: 'bg-emerald-500/90', - error: 'bg-red-500/90', - warning: 'bg-amber-500/90', - info: 'bg-blue-500/90', -}; - -/** - * 단일 토스트 아이템 - */ -function ToastItem({ toast, onClose }) { - const Icon = icons[toast.type] || icons.info; - - return ( - - - {toast.message} - - - ); -} - -/** - * 토스트 컨테이너 - * useUIStore와 연동하여 전역 토스트 관리 - */ -export default function ToastContainer() { - const { toasts, removeToast } = useUIStore(); - - return ( -
- - {toasts.map((toast) => ( - removeToast(toast.id)} - /> - ))} - -
- ); -} - -/** - * 단일 토스트 (레거시 호환) - */ -export function Toast({ toast, onClose }) { - if (!toast) return null; - - return ( - - - {toast.message} - - - ); -} diff --git a/frontend-temp/src/components/common/index.js b/frontend-temp/src/components/common/index.js deleted file mode 100644 index 1ec1edd..0000000 --- a/frontend-temp/src/components/common/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 공통 컴포넌트 export - */ -export { default as ErrorBoundary } from './ErrorBoundary'; -export { default as Loading, FullPageLoading, InlineLoading } from './Loading'; -export { default as ToastContainer, Toast } from './Toast'; -export { default as Lightbox } from './Lightbox'; diff --git a/frontend-temp/src/components/index.js b/frontend-temp/src/components/index.js deleted file mode 100644 index c799ef5..0000000 --- a/frontend-temp/src/components/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * 컴포넌트 통합 export - */ - -// 공통 컴포넌트 -export * from './common'; - -// 레이아웃 컴포넌트 -export * from './layout'; - -// 스케줄 컴포넌트 -export * from './schedule'; diff --git a/frontend-temp/src/components/layout/Footer.jsx b/frontend-temp/src/components/layout/Footer.jsx deleted file mode 100644 index afa25fc..0000000 --- a/frontend-temp/src/components/layout/Footer.jsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * PC용 푸터 컴포넌트 - */ -function Footer() { - const currentYear = new Date().getFullYear(); - - return ( -
-
-
-

© {currentYear} fromis_9 Fan Site. This is an unofficial fan-made website.

-
-
-
- ); -} - -export default Footer; diff --git a/frontend-temp/src/components/layout/Header.jsx b/frontend-temp/src/components/layout/Header.jsx deleted file mode 100644 index 40dc75f..0000000 --- a/frontend-temp/src/components/layout/Header.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import { NavLink } from 'react-router-dom'; -import { Instagram, Youtube } from 'lucide-react'; -import { SOCIAL_LINKS, NAV_ITEMS } from '@/constants'; - -/** - * X (Twitter) 아이콘 컴포넌트 - */ -const XIcon = ({ size = 20 }) => ( - - - -); - -/** - * PC용 헤더 컴포넌트 - */ -function Header() { - return ( -
-
-
- {/* 로고 */} - - fromis_9 - - - {/* 네비게이션 */} - - - {/* SNS 링크 */} - -
-
-
- ); -} - -export default Header; diff --git a/frontend-temp/src/components/layout/Layout.jsx b/frontend-temp/src/components/layout/Layout.jsx deleted file mode 100644 index 6a4e683..0000000 --- a/frontend-temp/src/components/layout/Layout.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import { useEffect } from 'react'; -import { useLocation, NavLink } from 'react-router-dom'; -import { useIsMobile } from '@/hooks'; -import Header from './Header'; -import Footer from './Footer'; -import MobileNav from './MobileNav'; - -/** - * 모바일 헤더 컴포넌트 - */ -function MobileHeader({ title, noShadow = false }) { - return ( -
-
- {title ? ( - {title} - ) : ( - - fromis_9 - - )} -
-
- ); -} - -/** - * 통합 레이아웃 컴포넌트 - * PC/Mobile 자동 분기 - * - * @param {object} props - * @param {React.ReactNode} props.children - 페이지 컨텐츠 - * @param {string} props.pageTitle - 모바일 헤더 타이틀 (없으면 fromis_9) - * @param {boolean} props.hideHeader - 헤더 숨김 여부 (자체 헤더가 있는 페이지) - * @param {boolean} props.hideFooter - 푸터 숨김 여부 - * @param {boolean} props.useCustomLayout - 자체 레이아웃 사용 (모바일) - * @param {boolean} props.noShadow - 모바일 헤더 그림자 숨김 - */ -function Layout({ - children, - pageTitle, - hideHeader = false, - hideFooter = false, - useCustomLayout = false, - noShadow = false, -}) { - const isMobile = useIsMobile(); - const location = useLocation(); - - // 모바일 레이아웃 활성화 (body 스크롤 방지) - useEffect(() => { - if (isMobile) { - document.documentElement.classList.add('mobile-layout'); - return () => { - document.documentElement.classList.remove('mobile-layout'); - }; - } - }, [isMobile]); - - // PC 레이아웃 - if (!isMobile) { - // Footer 숨김 페이지 (화면 고정 레이아웃) - const hideFooterPages = ['/schedule', '/members', '/album']; - const shouldHideFooter = hideFooter || hideFooterPages.some( - (path) => location.pathname === path || location.pathname.startsWith(path + '/') - ); - - // 일정 페이지에서는 스크롤바도 숨김 (내부에서 자체 스크롤 처리) - const isSchedulePage = location.pathname === '/schedule'; - - return ( -
- {!hideHeader &&
} -
-
- {children} -
- {!shouldHideFooter &&
} -
-
- ); - } - - // 모바일 레이아웃 - 자체 레이아웃 사용 시 (Schedule 페이지 등) - if (useCustomLayout) { - return ( -
- {children} - -
- ); - } - - // 모바일 레이아웃 - 기본 - return ( -
- {!hideHeader && } -
{children}
- -
- ); -} - -export default Layout; diff --git a/frontend-temp/src/components/layout/MobileNav.jsx b/frontend-temp/src/components/layout/MobileNav.jsx deleted file mode 100644 index dab72e3..0000000 --- a/frontend-temp/src/components/layout/MobileNav.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import { NavLink, useLocation } from 'react-router-dom'; -import { Home, Users, Disc3, Calendar } from 'lucide-react'; - -/** - * 모바일 하단 네비게이션 컴포넌트 - */ -function MobileNav() { - const location = useLocation(); - - const navItems = [ - { path: '/', label: '홈', icon: Home }, - { path: '/members', label: '멤버', icon: Users }, - { path: '/album', label: '앨범', icon: Disc3 }, - { path: '/schedule', label: '일정', icon: Calendar }, - ]; - - return ( - - ); -} - -export default MobileNav; diff --git a/frontend-temp/src/components/layout/index.js b/frontend-temp/src/components/layout/index.js deleted file mode 100644 index f43f04a..0000000 --- a/frontend-temp/src/components/layout/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 레이아웃 컴포넌트 export - */ -export { default as Layout } from './Layout'; -export { default as Header } from './Header'; -export { default as Footer } from './Footer'; -export { default as MobileNav } from './MobileNav'; diff --git a/frontend-temp/src/components/schedule/ScheduleCard.jsx b/frontend-temp/src/components/schedule/ScheduleCard.jsx deleted file mode 100644 index ccdfe72..0000000 --- a/frontend-temp/src/components/schedule/ScheduleCard.jsx +++ /dev/null @@ -1,233 +0,0 @@ -import { memo } from 'react'; -import { Clock, Tag, Link2, ExternalLink, Edit2, Trash2 } from 'lucide-react'; -import { cn, decodeHtmlEntities } from '@/utils'; -import { - getCategoryInfo, - getScheduleDate, - getScheduleTime, - getMemberList, -} from '@/utils/schedule'; -import { WEEKDAYS } from '@/constants'; - -/** - * 멤버 뱃지 컴포넌트 - */ -const MemberBadges = memo(function MemberBadges({ members }) { - if (!members || members.length === 0) return null; - - // 5명 이상이면 "프로미스나인"으로 표시 - if (members.length >= 5) { - return ( - - 프로미스나인 - - ); - } - - return members.map((member, i) => ( - - {typeof member === 'string' ? member : member.name} - - )); -}); - -/** - * 스케줄 카드 컴포넌트 - * PC/Mobile 공용, variant로 스타일 변경 - * - * @param {object} props - * @param {object} props.schedule - 스케줄 데이터 - * @param {'public'|'admin'} props.variant - 카드 스타일 (public: 공개 페이지용 카드, admin: 관리자 페이지용 리스트) - * @param {boolean} props.showDate - 날짜 표시 여부 - * @param {boolean} props.showYear - 연도 표시 여부 - * @param {function} props.onClick - 클릭 핸들러 - * @param {function} props.onEdit - 수정 버튼 클릭 핸들러 (admin variant) - * @param {function} props.onDelete - 삭제 버튼 클릭 핸들러 (admin variant) - * @param {string} props.className - 추가 클래스 - */ -const ScheduleCard = memo(function ScheduleCard({ - schedule, - variant = 'public', - showDate = true, - showYear = false, - onClick, - onEdit, - onDelete, - className, -}) { - const dateStr = getScheduleDate(schedule); - const date = dateStr ? new Date(dateStr) : null; - const categoryInfo = getCategoryInfo(schedule); - const timeStr = getScheduleTime(schedule); - const memberList = getMemberList(schedule); - const title = decodeHtmlEntities(schedule.title || ''); - - // 관리자 페이지용 리스트 스타일 - if (variant === 'admin') { - return ( -
-
- {/* 날짜 - 세로 중앙 정렬 */} - {showDate && date && ( -
- {showYear && ( -
- {date.getFullYear()}.{date.getMonth() + 1} -
- )} -
- {date.getDate()} -
-
- {WEEKDAYS[date.getDay()]}요일 -
-
- )} - - {/* 카테고리 바 */} -
- - {/* 내용 */} -
-

{title}

-
- {timeStr && ( - - - {timeStr} - - )} - - - {categoryInfo.name} - - {schedule.source?.name && ( - - - {schedule.source.name} - - )} -
- {memberList.length > 0 && ( -
- -
- )} -
- - {/* 액션 버튼 - 생일 일정이 아닐 때만 표시 */} - {!schedule.is_birthday && !String(schedule.id).startsWith('birthday-') && ( -
- {schedule.source?.url && ( - e.stopPropagation()} - className="p-2 hover:bg-blue-100 rounded-lg transition-colors text-blue-500" - > - - - )} - {onEdit && ( - - )} - {onDelete && ( - - )} -
- )} -
-
- ); - } - - // 공개 페이지용 카드 스타일 (기본) - return ( -
- {/* 좌측 날짜/카테고리 영역 */} -
- {date && ( - <> - {showYear && ( - - {date.getFullYear()}.{date.getMonth() + 1} - - )} - {date.getDate()} - {WEEKDAYS[date.getDay()]} - - )} -
- - {/* 우측 내용 영역 */} -
-

{title}

-
- {timeStr && ( - - - {timeStr} - - )} - - - {categoryInfo.name} - - {schedule.source?.name && ( - - - {schedule.source.name} - - )} -
- {memberList.length > 0 && ( -
- -
- )} -
-
- ); -}); - -export default ScheduleCard; diff --git a/frontend-temp/src/components/schedule/index.js b/frontend-temp/src/components/schedule/index.js deleted file mode 100644 index 02edebc..0000000 --- a/frontend-temp/src/components/schedule/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 스케줄 컴포넌트 export - */ -export { default as ScheduleCard } from './ScheduleCard'; diff --git a/frontend-temp/src/constants/index.js b/frontend-temp/src/constants/index.js index 53824e1..7b52724 100644 --- a/frontend-temp/src/constants/index.js +++ b/frontend-temp/src/constants/index.js @@ -57,11 +57,3 @@ export const MONTH_NAMES = [ '1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월', ]; - -/** 네비게이션 메뉴 항목 */ -export const NAV_ITEMS = [ - { path: '/', label: '홈' }, - { path: '/members', label: '멤버' }, - { path: '/album', label: '앨범' }, - { path: '/schedule', label: '일정' }, -]; diff --git a/frontend-temp/src/hooks/index.js b/frontend-temp/src/hooks/index.js index 3f93f14..c0447fe 100644 --- a/frontend-temp/src/hooks/index.js +++ b/frontend-temp/src/hooks/index.js @@ -24,9 +24,3 @@ export { useCalendar } from './useCalendar'; // 인증 export { useAdminAuth, useRedirectIfAuthenticated } from './useAdminAuth'; - -// 앨범 데이터 -export { useAlbums, useAlbumDetail, useAlbumGallery } from './useAlbumData'; - -// 멤버 데이터 -export { useMembers, useMemberDetail } from './useMemberData'; diff --git a/frontend-temp/src/hooks/useCalendar.js b/frontend-temp/src/hooks/useCalendar.js index 59e434c..8a2e190 100644 --- a/frontend-temp/src/hooks/useCalendar.js +++ b/frontend-temp/src/hooks/useCalendar.js @@ -5,17 +5,10 @@ import { getTodayKST } from '@/utils'; /** * 캘린더 훅 * 날짜 선택, 월 이동 등 캘린더 로직 제공 - * @param {Date|string} initialDate - 초기 날짜 + * @param {Date} initialDate - 초기 날짜 */ export function useCalendar(initialDate = new Date()) { - // initialDate가 Date 객체가 아니면 변환 - const ensureDate = (date) => { - if (date instanceof Date) return date; - if (typeof date === 'string') return new Date(date); - return new Date(); - }; - - const [currentDate, setCurrentDate] = useState(() => ensureDate(initialDate)); + const [currentDate, setCurrentDate] = useState(initialDate); const [selectedDate, setSelectedDate] = useState(getTodayKST()); const year = currentDate.getFullYear(); @@ -27,37 +20,6 @@ export function useCalendar(initialDate = new Date()) { const daysInMonth = new Date(year, month + 1, 0).getDate(); const prevMonthDays = new Date(year, month, 0).getDate(); - // 캘린더에 표시할 날짜 배열 생성 - const days = []; - - // 이전 달 날짜 - for (let i = firstDay - 1; i >= 0; i--) { - days.push({ - day: prevMonthDays - i, - isCurrentMonth: false, - date: new Date(year, month - 1, prevMonthDays - i), - }); - } - - // 현재 달 날짜 - for (let i = 1; i <= daysInMonth; i++) { - days.push({ - day: i, - isCurrentMonth: true, - date: new Date(year, month, i), - }); - } - - // 다음 달 날짜 (6주 채우기) - const remaining = 42 - days.length; // 6주 * 7일 = 42 - for (let i = 1; i <= remaining; i++) { - days.push({ - day: i, - isCurrentMonth: false, - date: new Date(year, month + 1, i), - }); - } - return { year, month, @@ -66,7 +28,6 @@ export function useCalendar(initialDate = new Date()) { daysInMonth, prevMonthDays, weekdays: WEEKDAYS, - days, }; }, [year, month]); @@ -132,7 +93,6 @@ export function useCalendar(initialDate = new Date()) { ...calendarData, currentDate, selectedDate, - canGoPrev: canGoPrevMonth, canGoPrevMonth, goToPrevMonth, goToNextMonth, diff --git a/frontend-temp/src/pages/album/Album.jsx b/frontend-temp/src/pages/album/Album.jsx deleted file mode 100644 index ee8d8fa..0000000 --- a/frontend-temp/src/pages/album/Album.jsx +++ /dev/null @@ -1,188 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import { motion } from 'framer-motion'; -import { Calendar, Music } from 'lucide-react'; -import { useIsMobile } from '@/hooks'; -import { useAlbums } from '@/hooks'; -import { Loading } from '@/components'; -import { formatDate } from '@/utils'; - -/** - * 앨범 목록 페이지 (PC/Mobile 통합) - */ -function Album() { - const navigate = useNavigate(); - const isMobile = useIsMobile(); - - // useQuery로 앨범 데이터 로드 - const { data: albums = [], isLoading } = useAlbums(); - - // 타이틀곡 찾기 - const getTitleTrack = (tracks) => { - if (!tracks || tracks.length === 0) return ''; - const titleTrack = tracks.find((t) => t.is_title_track); - return titleTrack ? titleTrack.title : tracks[0].title; - }; - - // 앨범 타입 (short 우선) - const getAlbumType = (album) => album.album_type_short || album.album_type; - - // 앨범 통계 - const albumStats = { - 정규: albums.filter((a) => getAlbumType(a) === '정규').length, - 미니: albums.filter((a) => getAlbumType(a) === '미니').length, - 싱글: albums.filter((a) => getAlbumType(a) === '싱글').length, - 총: albums.length, - }; - - // 앨범 클릭 핸들러 - const handleAlbumClick = (album) => { - const path = isMobile ? album.folder_name : encodeURIComponent(album.title); - navigate(`/album/${path}`); - }; - - if (isLoading) { - return ( -
- -
- ); - } - - // 모바일 레이아웃 - if (isMobile) { - return ( -
-
- {albums.map((album, index) => ( - handleAlbumClick(album)} - className="bg-white rounded-2xl overflow-hidden shadow-md cursor-pointer" - > -
- {album.cover_thumb_url && ( - {album.title} - )} -
-
-

{album.title}

-

- {getAlbumType(album)} · {album.release_date?.slice(0, 4)} -

-
-
- ))} -
-
- ); - } - - // PC 레이아웃 - return ( -
-
- {/* 헤더 */} -
- - 앨범 - - - 프로미스나인의 음악을 만나보세요 - -
- - {/* 통계 */} - -
-

{albumStats.정규}

-

정규 앨범

-
-
-

{albumStats.미니}

-

미니 앨범

-
-
-

{albumStats.싱글}

-

싱글 앨범

-
-
-

{albumStats.총}

-

총 앨범

-
-
- - {/* 앨범 그리드 */} -
- {albums.map((album, index) => ( - handleAlbumClick(album)} - > - {/* 앨범 커버 */} -
- {album.title} - - {/* 호버 오버레이 */} -
-
- -

{album.tracks?.length || 0}곡 수록

-
-
-
- - {/* 앨범 정보 */} -
-
-

{album.title}

- - {getAlbumType(album)} - -
-

- {getTitleTrack(album.tracks)} -

-
- - {formatDate(album.release_date, 'YYYY.MM.DD')} -
-
-
- ))} -
-
-
- ); -} - -export default Album; diff --git a/frontend-temp/src/pages/album/index.js b/frontend-temp/src/pages/album/index.js deleted file mode 100644 index 64aae39..0000000 --- a/frontend-temp/src/pages/album/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 앨범 페이지 export - */ -export { default as Album } from './Album'; diff --git a/frontend-temp/src/pages/common/NotFound.jsx b/frontend-temp/src/pages/common/NotFound.jsx deleted file mode 100644 index 0fb2393..0000000 --- a/frontend-temp/src/pages/common/NotFound.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Link } from 'react-router-dom'; -import { Home } from 'lucide-react'; -import { useIsMobile } from '@/hooks'; - -/** - * 404 페이지 (PC/Mobile 통합) - */ -function NotFound() { - const isMobile = useIsMobile(); - - return ( -
-

- 404 -

-

- 페이지를 찾을 수 없습니다 -

- - - 홈으로 돌아가기 - -
- ); -} - -export default NotFound; diff --git a/frontend-temp/src/pages/common/index.js b/frontend-temp/src/pages/common/index.js deleted file mode 100644 index 4d20aa9..0000000 --- a/frontend-temp/src/pages/common/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as NotFound } from './NotFound'; diff --git a/frontend-temp/src/pages/home/Home.jsx b/frontend-temp/src/pages/home/Home.jsx deleted file mode 100644 index 5e24af7..0000000 --- a/frontend-temp/src/pages/home/Home.jsx +++ /dev/null @@ -1,333 +0,0 @@ -import { Link } from 'react-router-dom'; -import { motion } from 'framer-motion'; -import { Calendar, ArrowRight, Clock, Tag, Music } from 'lucide-react'; -import { useIsMobile, useMembers, useAlbums, useUpcomingSchedules } from '@/hooks'; -import { Loading } from '@/components'; - -/** - * 홈 페이지 (PC/Mobile 통합) - */ -function Home() { - const isMobile = useIsMobile(); - - // 멤버 데이터 - const { data: members = [] } = useMembers(); - - // 앨범 데이터 (최신 4개) - const { data: allAlbums = [] } = useAlbums(); - const albums = allAlbums.slice(0, 4); - - // 다가오는 일정 (3개) - const { data: upcomingSchedules = [] } = useUpcomingSchedules(3); - - // D+Day 계산 - const debutDate = new Date('2018-01-24'); - const today = new Date(); - const dDay = Math.floor((today - debutDate) / (1000 * 60 * 60 * 24)) + 1; - - // 모바일 레이아웃 - if (isMobile) { - return ( -
- {/* 히어로 */} -
-

fromis_9

-

프로미스나인

-
- - {/* 통계 */} -
- {[ - { value: '2018.01.24', label: '데뷔일' }, - { value: `D+${dDay.toLocaleString()}`, label: 'D+Day' }, - ].map((stat, i) => ( -
-

{stat.value}

-

{stat.label}

-
- ))} -
- - {/* 멤버 미리보기 */} -
-
-

멤버

- - 전체보기 - -
-
- {members.filter((m) => !m.is_former).map((member) => ( -
-
- {member.name} -
-

{member.name}

-
- ))} -
-
- - {/* 앨범 미리보기 */} -
-
-

앨범

- - 전체보기 - -
-
- {albums.slice(0, 2).map((album) => ( - -
- {album.title} -
-

{album.title}

- - ))} -
-
- - {/* 일정 미리보기 */} -
-
-

다가오는 일정

- - 전체보기 - -
- {upcomingSchedules.length === 0 ? ( -
- -

예정된 일정이 없습니다

-
- ) : ( -
- {upcomingSchedules.map((schedule) => ( -
-

{schedule.title}

-

- {new Date(schedule.date).getMonth() + 1}월 {new Date(schedule.date).getDate()}일 - {schedule.time && ` · ${schedule.time.slice(0, 5)}`} -

-
- ))} -
- )} -
-
- ); - } - - // PC 레이아웃 - return ( -
- {/* 히어로 섹션 */} -
-
-
- -

fromis_9

-

프로미스나인

-

- 인사드리겠습니다. 둘, 셋! -
- 이제는 약속해 소중히 간직해, -
- 당신의 아이돌로 성장하겠습니다! -

-
-
-
-
-
-
-
- - {/* 그룹 통계 */} -
-
-
- {[ - { value: '2018.01.24', label: '데뷔일' }, - { value: `D+${dDay.toLocaleString()}`, label: 'D+Day' }, - { value: '5', label: '멤버 수' }, - { value: 'flover', label: '팬덤명' }, - ].map((stat, index) => ( - -

{stat.value}

-

{stat.label}

-
- ))} -
-
-
- - {/* 멤버 미리보기 */} -
-
-
-

멤버

- - 전체보기 - -
-
- {members.filter((m) => !m.is_former).map((member, index) => ( - -
- {member.name} -
-
-
-

{member.name}

-
- - ))} -
-
-
- - {/* 앨범 미리보기 */} -
-
-
-

앨범

- - 전체보기 - -
-
- {albums.map((album, index) => ( - (window.location.href = `/album/${encodeURIComponent(album.title)}`)} - > -
- {album.title} -
-
- -

{album.tracks?.length || 0}곡 수록

-
-
-
-
-

{album.title}

-

{album.release_date?.slice(0, 4)}

-
-
- ))} -
-
-
- - {/* 일정 미리보기 */} -
-
-
-

다가오는 일정

- - 전체보기 - -
- {upcomingSchedules.length === 0 ? ( -
- -

예정된 일정이 없습니다

-
- ) : ( -
- {upcomingSchedules.map((schedule) => { - const scheduleDate = new Date(schedule.date); - const categoryColor = schedule.category_color || '#6366f1'; - const memberList = schedule.member_names - ? schedule.member_names.split(',') - : schedule.members?.map((m) => m.name) || []; - - return ( - -
- {scheduleDate.getDate()} - - {['일', '월', '화', '수', '목', '금', '토'][scheduleDate.getDay()]} - -
-
-

{schedule.title}

-
- {schedule.time && ( - - - {schedule.time.slice(0, 5)} - - )} - - - {schedule.category_name} - -
- {memberList.length > 0 && ( -
- {memberList.map((name, i) => ( - - {name.trim()} - - ))} -
- )} -
-
- ); - })} -
- )} -
-
-
- ); -} - -export default Home; diff --git a/frontend-temp/src/pages/home/index.js b/frontend-temp/src/pages/home/index.js deleted file mode 100644 index c2d92a5..0000000 --- a/frontend-temp/src/pages/home/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Home } from './Home'; diff --git a/frontend-temp/src/pages/index.js b/frontend-temp/src/pages/index.js deleted file mode 100644 index 558eff9..0000000 --- a/frontend-temp/src/pages/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * 페이지 export - */ -export * from './schedule'; -export * from './album'; -export * from './home'; -export * from './members'; -export * from './common'; diff --git a/frontend-temp/src/pages/members/Members.jsx b/frontend-temp/src/pages/members/Members.jsx deleted file mode 100644 index 8ad7a4a..0000000 --- a/frontend-temp/src/pages/members/Members.jsx +++ /dev/null @@ -1,181 +0,0 @@ -import { motion } from 'framer-motion'; -import { Instagram, Calendar } from 'lucide-react'; -import { useIsMobile, useMembers } from '@/hooks'; -import { Loading } from '@/components'; -import { formatDate } from '@/utils'; - -/** - * 멤버 카드 컴포넌트 (PC) - */ -function MemberCard({ member, isFormer = false, delay = 0 }) { - return ( - -
- {/* 이미지 */} -
- {member.name} -
- - {/* 정보 */} -
-

- {member.name} -

- -
- - {formatDate(member.birth_date, 'YYYY.MM.DD')} -
- - {/* 인스타그램 링크 */} - {member.instagram && !isFormer && ( - - - Instagram - - )} -
- - {/* 호버 효과 - 컬러 바 */} -
-
- - ); -} - -/** - * 멤버 페이지 (PC/Mobile 통합) - */ -function Members() { - const isMobile = useIsMobile(); - const { data: members = [], isLoading } = useMembers(); - - const currentMembers = members.filter((m) => !m.is_former); - const formerMembers = members.filter((m) => m.is_former); - - if (isLoading) { - return ( -
- -
- ); - } - - // 모바일 레이아웃 - if (isMobile) { - return ( -
- {/* 현재 멤버 */} -
- {currentMembers.map((member) => ( -
-
- {member.name} -
-
-

{member.name}

-

{formatDate(member.birth_date, 'YYYY.MM.DD')}

-
-
- ))} -
- - {/* 전 멤버 */} - {formerMembers.length > 0 && ( - <> -

전 멤버

-
- {formerMembers.map((member) => ( -
-
- {member.name} -
-
-

{member.name}

-

{formatDate(member.birth_date, 'YYYY.MM.DD')}

-
-
- ))} -
- - )} -
- ); - } - - // PC 레이아웃 - return ( -
-
- {/* 헤더 */} -
- - 멤버 - - - 프로미스나인의 멤버를 소개합니다 - -
- - {/* 현재 멤버 그리드 */} -
- {currentMembers.map((member, index) => ( - - ))} -
- - {/* 전 멤버 섹션 */} - {formerMembers.length > 0 && ( - -

전 멤버

-
- {formerMembers.map((member, index) => ( - - ))} -
-
- )} -
-
- ); -} - -export default Members; diff --git a/frontend-temp/src/pages/members/index.js b/frontend-temp/src/pages/members/index.js deleted file mode 100644 index c1d2028..0000000 --- a/frontend-temp/src/pages/members/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Members } from './Members'; diff --git a/frontend-temp/src/pages/schedule/Schedule.jsx b/frontend-temp/src/pages/schedule/Schedule.jsx deleted file mode 100644 index 4fb6827..0000000 --- a/frontend-temp/src/pages/schedule/Schedule.jsx +++ /dev/null @@ -1,330 +0,0 @@ -import { useState, useMemo, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { ChevronLeft, ChevronRight, Search } from 'lucide-react'; -import { useIsMobile, useScheduleData, useCategories, useCalendar } from '@/hooks'; -import { useScheduleStore } from '@/stores'; -import { Loading, ScheduleCard } from '@/components'; -import { cn, getTodayKST, decodeHtmlEntities } from '@/utils'; -import { WEEKDAYS, MIN_YEAR } from '@/constants'; - -/** - * PC 캘린더 컴포넌트 - */ -function PCCalendar({ selectedDate, schedules, categories, onSelectDate, onMonthChange }) { - const { days, year, month, canGoPrev } = useCalendar(selectedDate); - - // 날짜별 일정 맵 - const scheduleDates = useMemo(() => { - const dateMap = {}; - schedules?.forEach((schedule) => { - const date = schedule.date?.split('T')[0]; - if (!dateMap[date]) dateMap[date] = []; - const cat = categories?.find((c) => c.id === (schedule.category_id || schedule.category?.id)); - dateMap[date].push(cat?.color || '#6b7280'); - }); - return dateMap; - }, [schedules, categories]); - - const isToday = (date) => { - const today = new Date(); - return ( - date.getDate() === today.getDate() && - date.getMonth() === today.getMonth() && - date.getFullYear() === today.getFullYear() - ); - }; - - const isSelected = (date) => { - const sel = new Date(selectedDate); - return ( - date.getDate() === sel.getDate() && - date.getMonth() === sel.getMonth() && - date.getFullYear() === sel.getFullYear() - ); - }; - - const formatDateStr = (date) => { - const y = date.getFullYear(); - const m = String(date.getMonth() + 1).padStart(2, '0'); - const d = String(date.getDate()).padStart(2, '0'); - return `${y}-${m}-${d}`; - }; - - return ( -
- {/* 헤더 */} -
- -

- {year}년 {month + 1}월 -

- -
- - {/* 요일 헤더 */} -
- {WEEKDAYS.map((day, i) => ( -
- {day} -
- ))} -
- - {/* 날짜 그리드 */} -
- {days.map((item, index) => { - const dayOfWeek = index % 7; - const dateStr = formatDateStr(item.date); - const colors = scheduleDates[dateStr] || []; - - return ( - - ); - })} -
-
- ); -} - -/** - * 스케줄 페이지 (PC/Mobile 통합) - */ -function Schedule() { - const navigate = useNavigate(); - const isMobile = useIsMobile(); - - // Zustand store - const { currentDate: storedCurrentDate, setCurrentDate, selectedDate, setSelectedDate } = useScheduleStore(); - - // 초기값 설정 - currentDate가 Date 객체가 아닐 수 있으므로 안전하게 변환 - const today = getTodayKST(); - const actualSelectedDate = selectedDate || today; - const currentDate = storedCurrentDate instanceof Date ? storedCurrentDate : new Date(storedCurrentDate || today); - - // 데이터 로드 - const year = currentDate.getFullYear(); - const month = currentDate.getMonth() + 1; - const { data: schedules, isLoading: schedulesLoading } = useScheduleData(year, month); - const { data: categories } = useCategories(); - - // 선택된 날짜의 일정 - const selectedDateSchedules = useMemo(() => { - if (!schedules) return []; - const sel = new Date(actualSelectedDate); - const dateStr = `${sel.getFullYear()}-${String(sel.getMonth() + 1).padStart(2, '0')}-${String(sel.getDate()).padStart(2, '0')}`; - return schedules - .filter((s) => s.date?.split('T')[0] === dateStr) - .sort((a, b) => { - const aIsBirthday = a.is_birthday || String(a.id).startsWith('birthday-'); - const bIsBirthday = b.is_birthday || String(b.id).startsWith('birthday-'); - if (aIsBirthday && !bIsBirthday) return -1; - if (!aIsBirthday && bIsBirthday) return 1; - return 0; - }); - }, [schedules, actualSelectedDate]); - - // 월 변경 - const changeMonth = (delta) => { - const newDate = new Date(currentDate); - newDate.setMonth(newDate.getMonth() + delta); - - // 2017년 1월 이전으로 이동 불가 - if (newDate.getFullYear() < MIN_YEAR || (newDate.getFullYear() === MIN_YEAR && newDate.getMonth() < 0)) { - return; - } - - setCurrentDate(newDate); - - // 이번 달이면 오늘 날짜, 다른 달이면 1일 선택 - const now = new Date(); - if (newDate.getFullYear() === now.getFullYear() && newDate.getMonth() === now.getMonth()) { - setSelectedDate(getTodayKST()); - } else { - const firstDay = `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}-01`; - setSelectedDate(firstDay); - } - }; - - // 날짜 선택 - const handleSelectDate = (date) => { - const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; - setSelectedDate(dateStr); - - // 월이 다르면 currentDate도 변경 - if (date.getMonth() !== currentDate.getMonth() || date.getFullYear() !== currentDate.getFullYear()) { - setCurrentDate(date); - } - }; - - // PC 레이아웃 - if (!isMobile) { - return ( -
- {/* 좌측: 캘린더 */} -
- -
- - {/* 우측: 일정 목록 */} -
- {/* 헤더 */} -
-
-

- {new Date(actualSelectedDate).getMonth() + 1}월{' '} - {new Date(actualSelectedDate).getDate()}일{' '} - {WEEKDAYS[new Date(actualSelectedDate).getDay()]}요일 -

- - {selectedDateSchedules.length}개의 일정 - -
- -
- - {/* 일정 목록 */} -
- {schedulesLoading ? ( -
- -
- ) : selectedDateSchedules.length === 0 ? ( -
- {new Date(actualSelectedDate).getMonth() + 1}월{' '} - {new Date(actualSelectedDate).getDate()}일 일정이 없습니다 -
- ) : ( -
- {selectedDateSchedules.map((schedule) => ( - navigate(`/schedule/${schedule.id}`)} - /> - ))} -
- )} -
-
-
- ); - } - - // 모바일 레이아웃 (간소화된 버전) - return ( - <> - {/* 모바일 툴바 */} -
-
- - - {currentDate.getFullYear()}년 {currentDate.getMonth() + 1}월 - -
- - -
-
-
- - {/* 모바일 컨텐츠 */} -
-
- {schedulesLoading ? ( -
- -
- ) : selectedDateSchedules.length === 0 ? ( -
- {new Date(actualSelectedDate).getMonth() + 1}월{' '} - {new Date(actualSelectedDate).getDate()}일 일정이 없습니다 -
- ) : ( -
- {selectedDateSchedules.map((schedule) => ( - navigate(`/schedule/${schedule.id}`)} - /> - ))} -
- )} -
-
- - ); -} - -export default Schedule; diff --git a/frontend-temp/src/pages/schedule/index.js b/frontend-temp/src/pages/schedule/index.js deleted file mode 100644 index b11430b..0000000 --- a/frontend-temp/src/pages/schedule/index.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 스케줄 페이지 export - */ -export { default as Schedule } from './Schedule';