2026-01-21 17:54:27 +09:00
|
|
|
import { BrowserRouter, Routes, Route, NavLink } from "react-router-dom";
|
2026-01-21 17:11:00 +09:00
|
|
|
import { cn, getTodayKST, formatFullDate } from "@/utils";
|
2026-01-21 17:39:48 +09:00
|
|
|
import { useUIStore } from "@/stores";
|
|
|
|
|
import { useIsMobile, useCategories, useScheduleData } from "@/hooks";
|
2026-01-21 17:54:27 +09:00
|
|
|
import { ErrorBoundary, Loading, ToastContainer, ScheduleCard, Layout } from "@/components";
|
2026-01-21 18:03:06 +09:00
|
|
|
import { Schedule, Album } from "@/pages";
|
2026-01-21 17:04:18 +09:00
|
|
|
|
|
|
|
|
/**
|
2026-01-21 17:54:27 +09:00
|
|
|
* 홈 페이지 (임시)
|
2026-01-21 17:04:18 +09:00
|
|
|
*/
|
2026-01-21 17:54:27 +09:00
|
|
|
function Home() {
|
2026-01-21 17:07:56 +09:00
|
|
|
const today = getTodayKST();
|
2026-01-21 17:22:38 +09:00
|
|
|
const isMobile = useIsMobile();
|
2026-01-21 17:39:48 +09:00
|
|
|
const { showSuccess, showError } = useUIStore();
|
2026-01-21 17:22:38 +09:00
|
|
|
const { data: categories, isLoading: categoriesLoading } = useCategories();
|
2026-01-21 17:39:48 +09:00
|
|
|
const currentDate = new Date();
|
|
|
|
|
const { data: schedules, isLoading: schedulesLoading } = useScheduleData(
|
|
|
|
|
currentDate.getFullYear(),
|
|
|
|
|
currentDate.getMonth() + 1
|
|
|
|
|
);
|
2026-01-21 17:17:56 +09:00
|
|
|
|
2026-01-21 17:54:27 +09:00
|
|
|
return (
|
|
|
|
|
<div className={cn("p-4", isMobile ? "pb-20" : "")}>
|
|
|
|
|
<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 7 진행 중 - 스케줄 페이지</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 className="border-t pt-3">
|
|
|
|
|
<p className="font-semibold mb-2">페이지 이동</p>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<NavLink
|
|
|
|
|
to="/schedule"
|
|
|
|
|
className="px-3 py-1 bg-primary text-white rounded text-xs"
|
|
|
|
|
>
|
|
|
|
|
일정 페이지
|
|
|
|
|
</NavLink>
|
|
|
|
|
</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>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 프로미스나인 팬사이트 메인 앱
|
|
|
|
|
*
|
2026-01-21 18:03:06 +09:00
|
|
|
* Phase 8: 앨범 페이지 마이그레이션
|
|
|
|
|
* - Album 페이지 (PC/Mobile 통합)
|
|
|
|
|
* - useAlbums 훅 추가
|
2026-01-21 17:54:27 +09:00
|
|
|
*/
|
|
|
|
|
function App() {
|
2026-01-21 17:04:18 +09:00
|
|
|
return (
|
|
|
|
|
<BrowserRouter>
|
2026-01-21 17:39:48 +09:00
|
|
|
<ErrorBoundary>
|
|
|
|
|
<Routes>
|
2026-01-21 17:54:27 +09:00
|
|
|
{/* 홈 */}
|
2026-01-21 17:39:48 +09:00
|
|
|
<Route
|
|
|
|
|
path="/"
|
|
|
|
|
element={
|
2026-01-21 17:54:27 +09:00
|
|
|
<Layout>
|
|
|
|
|
<Home />
|
|
|
|
|
<ToastContainer />
|
|
|
|
|
</Layout>
|
|
|
|
|
}
|
|
|
|
|
/>
|
2026-01-21 17:11:00 +09:00
|
|
|
|
2026-01-21 17:54:27 +09:00
|
|
|
{/* 스케줄 */}
|
|
|
|
|
<Route
|
|
|
|
|
path="/schedule"
|
|
|
|
|
element={
|
|
|
|
|
<Layout useCustomLayout>
|
|
|
|
|
<Schedule />
|
2026-01-21 17:39:48 +09:00
|
|
|
<ToastContainer />
|
2026-01-21 17:54:27 +09:00
|
|
|
</Layout>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{/* 임시 페이지들 */}
|
|
|
|
|
<Route
|
|
|
|
|
path="/members"
|
|
|
|
|
element={
|
|
|
|
|
<Layout pageTitle="멤버">
|
|
|
|
|
<div className="p-4 text-center text-gray-500">멤버 페이지 (준비 중)</div>
|
|
|
|
|
</Layout>
|
|
|
|
|
}
|
|
|
|
|
/>
|
2026-01-21 18:03:06 +09:00
|
|
|
{/* 앨범 */}
|
2026-01-21 17:54:27 +09:00
|
|
|
<Route
|
|
|
|
|
path="/album"
|
|
|
|
|
element={
|
|
|
|
|
<Layout pageTitle="앨범">
|
2026-01-21 18:03:06 +09:00
|
|
|
<Album />
|
|
|
|
|
<ToastContainer />
|
2026-01-21 17:54:27 +09:00
|
|
|
</Layout>
|
2026-01-21 17:39:48 +09:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</Routes>
|
|
|
|
|
</ErrorBoundary>
|
2026-01-21 17:04:18 +09:00
|
|
|
</BrowserRouter>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App;
|