fromis_9/frontend-temp/src/App.jsx

148 lines
6.1 KiB
React
Raw Normal View History

import { BrowserRouter, Routes, Route } from "react-router-dom";
import { cn, getTodayKST, formatFullDate } from "@/utils";
import { useUIStore } from "@/stores";
import { useIsMobile, useCategories, useScheduleData } from "@/hooks";
import { ErrorBoundary, Loading, ToastContainer, ScheduleCard } from "@/components";
/**
* 프로미스나인 팬사이트 메인
*
* Phase 6: 공통 컴포넌트 완료
* - ErrorBoundary: 에러 경계
* - Loading, FullPageLoading, InlineLoading: 로딩 스피너
* - Toast, ToastContainer: 토스트 알림
* - Lightbox: 이미지 라이트박스
* - ScheduleCard: 스케줄 카드 (list/card/compact)
*/
function App() {
const today = getTodayKST();
const isMobile = useIsMobile();
const { showSuccess, showError } = useUIStore();
// 커스텀 훅 사용
const { data: categories, isLoading: categoriesLoading } = useCategories();
const currentDate = new Date();
const { data: schedules, isLoading: schedulesLoading } = useScheduleData(
currentDate.getFullYear(),
currentDate.getMonth() + 1
);
return (
<BrowserRouter>
<ErrorBoundary>
<Routes>
<Route
path="/"
element={
<div className="min-h-screen bg-gray-50 p-4">
<div className="max-w-2xl mx-auto space-y-4">
<div className="text-center">
<h1 className="text-2xl font-bold text-primary mb-2">
fromis_9 Frontend Refactoring
</h1>
<p className="text-gray-600">Phase 6 완료 - 공통 컴포넌트</p>
<p className={cn("text-sm", isMobile ? "text-blue-500" : "text-green-500")}>
디바이스: {isMobile ? "모바일" : "PC"}
</p>
</div>
<div className="p-4 bg-white rounded-lg shadow text-sm space-y-3">
<p><strong>오늘:</strong> {formatFullDate(today)}</p>
<div className="border-t pt-3">
<p className="font-semibold mb-2">카테고리 ({categories?.length || 0})</p>
{categoriesLoading ? (
<Loading size="sm" />
) : (
<div className="flex flex-wrap gap-1">
{categories?.map((c) => (
<span
key={c.id}
className="px-2 py-0.5 rounded text-xs text-white"
style={{ backgroundColor: c.color }}
>
{c.name}
</span>
))}
</div>
)}
</div>
<div className="border-t pt-3">
<p className="font-semibold mb-2">토스트 테스트</p>
<div className="flex gap-2">
<button
onClick={() => showSuccess("성공 메시지!")}
className="px-3 py-1 bg-emerald-500 text-white rounded text-xs"
>
성공
</button>
<button
onClick={() => showError("에러 메시지!")}
className="px-3 py-1 bg-red-500 text-white rounded text-xs"
>
에러
</button>
</div>
</div>
</div>
{/* ScheduleCard 컴포넌트 테스트 */}
{schedulesLoading ? (
<div className="p-4 bg-white rounded-lg shadow">
<Loading size="sm" text="스케줄 로딩 중..." />
</div>
) : schedules?.length > 0 ? (
<>
{/* Public Variant (공개 페이지용) */}
<div className="p-4 bg-white rounded-lg shadow">
<p className="font-semibold mb-3 text-sm">variant="public" (공개 페이지용)</p>
<div className="space-y-3">
{schedules.slice(0, 2).map((schedule) => (
<ScheduleCard
key={schedule.id}
schedule={schedule}
variant="public"
onClick={() => showSuccess(`${schedule.title} 클릭`)}
/>
))}
</div>
</div>
{/* Admin Variant (관리자 페이지용) */}
<div className="p-4 bg-white rounded-lg shadow">
<p className="font-semibold mb-3 text-sm">variant="admin" (관리자 페이지용)</p>
<div className="divide-y">
{schedules.slice(0, 2).map((schedule) => (
<ScheduleCard
key={schedule.id}
schedule={schedule}
variant="admin"
onClick={() => showSuccess(`${schedule.title} 클릭`)}
onEdit={(s) => showSuccess(`${s.title} 수정`)}
onDelete={(s) => showError(`${s.title} 삭제`)}
/>
))}
</div>
</div>
</>
) : (
<div className="p-4 bg-white rounded-lg shadow">
<p className="text-gray-500 text-sm">이번 스케줄이 없습니다.</p>
</div>
)}
</div>
{/* 토스트 컨테이너 */}
<ToastContainer />
</div>
}
/>
</Routes>
</ErrorBoundary>
</BrowserRouter>
);
}
export default App;