diff --git a/README.md b/README.md index 3a2a8b4..9d3ee8d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Galaxy-PO +# Galaxis-Po Integrated Quant Portfolio Management Application @@ -37,7 +37,7 @@ npm run dev ## Project Structure ``` -galaxy-po/ +galaxis-po/ ├── backend/ # FastAPI backend ├── frontend/ # Next.js frontend ├── docker-compose.yml diff --git a/backend/app/core/config.py b/backend/app/core/config.py index db1b4f4..a24bc27 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -7,7 +7,7 @@ from functools import lru_cache class Settings(BaseSettings): # Application - app_name: str = "Galaxy-PO" + app_name: str = "Galaxis-Po" debug: bool = False # Database diff --git a/backend/app/main.py b/backend/app/main.py index 16f4753..9e106f2 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,5 +1,5 @@ """ -Galaxy-PO Backend API +Galaxis-Po Backend API """ import logging from contextlib import asynccontextmanager @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) async def lifespan(app: FastAPI): """Application lifespan manager.""" # Startup - logger.info("Starting Galaxy-PO API...") + logger.info("Starting Galaxis-Po API...") # Start scheduler (import here to avoid circular imports) try: @@ -37,7 +37,7 @@ async def lifespan(app: FastAPI): yield # Shutdown - logger.info("Shutting down Galaxy-PO API...") + logger.info("Shutting down Galaxis-Po API...") try: from jobs.scheduler import stop_scheduler @@ -48,7 +48,7 @@ async def lifespan(app: FastAPI): app = FastAPI( - title="Galaxy-PO API", + title="Galaxis-Po API", description="Quant Portfolio Management API", version="0.1.0", lifespan=lifespan, diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 0daf15f..c039e4d 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,7 +1,7 @@ [project] -name = "galaxy-po-backend" +name = "galaxis-po-backend" version = "1.0.0" -description = "Galaxy Portfolio Optimization Backend" +description = "Galaxis Portfolio Optimization Backend" requires-python = ">=3.12" dependencies = [ "fastapi==0.115.6", @@ -9,12 +9,14 @@ dependencies = [ "sqlalchemy==2.0.36", "alembic==1.14.0", "psycopg2-binary==2.9.10", - "pydantic==2.10.4", + "pydantic[email]==2.10.4", "pydantic-settings==2.7.1", "python-jose[cryptography]==3.3.0", "passlib[bcrypt]==1.7.4", + "bcrypt==4.0.1", "python-multipart==0.0.20", "apscheduler==3.10.4", + "setuptools", "pykrx==1.0.45", "requests==2.32.3", "beautifulsoup4==4.12.3", diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 288819d..3bbc8d4 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -4,7 +4,7 @@ services: postgres: image: postgres:15-alpine - container_name: galaxy-po-db + container_name: galaxis-po-db environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} @@ -24,7 +24,7 @@ services: build: context: ./backend dockerfile: Dockerfile - container_name: galaxy-po-backend + container_name: galaxis-po-backend env_file: - .env.prod environment: @@ -42,7 +42,7 @@ services: context: ./frontend dockerfile: Dockerfile target: production - container_name: galaxy-po-frontend + container_name: galaxis-po-frontend environment: NEXT_PUBLIC_API_URL: ${API_URL} depends_on: @@ -54,7 +54,7 @@ services: nginx: image: nginx:alpine - container_name: galaxy-po-nginx + container_name: galaxis-po-nginx ports: - "80:80" - "443:443" diff --git a/docker-compose.yml b/docker-compose.yml index 7c20339..196fb3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: postgres: image: postgres:15-alpine - container_name: galaxy-po-db + container_name: galaxis-po-db environment: POSTGRES_USER: ${DB_USER:-galaxy} POSTGRES_PASSWORD: ${DB_PASSWORD:-devpassword} @@ -21,7 +21,7 @@ services: build: context: ./backend dockerfile: Dockerfile - container_name: galaxy-po-backend + container_name: galaxis-po-backend env_file: - .env environment: @@ -50,7 +50,7 @@ services: context: ./frontend dockerfile: Dockerfile target: development - container_name: galaxy-po-frontend + container_name: galaxis-po-frontend environment: NEXT_PUBLIC_API_URL: http://localhost:8000 ports: diff --git a/docs/plans/2026-02-05-phase1-foundation.md b/docs/plans/2026-02-05-phase1-foundation.md new file mode 100644 index 0000000..d15c7b3 --- /dev/null +++ b/docs/plans/2026-02-05-phase1-foundation.md @@ -0,0 +1,611 @@ +# Phase 1: 기반 작업 구현 계획 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** shadcn/ui, next-themes, Lucide Icons를 설치하고 다크/라이트 테마 시스템을 구축한다. + +**Architecture:** Next.js App Router의 layout.tsx에 ThemeProvider를 적용하고, shadcn/ui의 CSS 변수 기반 테마 시스템을 사용한다. Tailwind CSS 4 환경에 맞게 설정한다. + +**Tech Stack:** Next.js 16, React 19, Tailwind CSS 4, shadcn/ui, next-themes, lucide-react + +--- + +## Task 1: shadcn/ui 초기화 및 설정 + +**Files:** +- Create: `frontend/components.json` +- Create: `frontend/src/lib/utils.ts` +- Modify: `frontend/package.json` +- Modify: `frontend/src/app/globals.css` +- Modify: `frontend/tailwind.config.ts` (새로 생성) + +**Step 1: 필수 의존성 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm install class-variance-authority clsx tailwind-merge +npm install -D tailwindcss@latest postcss autoprefixer +``` + +Expected: 패키지 설치 완료 + +**Step 2: tailwind.config.ts 생성** + +Create `frontend/tailwind.config.ts`: + +```typescript +import type { Config } from "tailwindcss"; + +const config: Config = { + darkMode: ["class"], + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + }, + }, + plugins: [], +}; + +export default config; +``` + +**Step 3: postcss.config.mjs 수정** + +Modify `frontend/postcss.config.mjs`: + +```javascript +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +export default config; +``` + +**Step 4: globals.css를 shadcn/ui 테마로 교체** + +Replace `frontend/src/app/globals.css`: + +```css +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} +``` + +**Step 5: utils.ts 생성** + +Create `frontend/src/lib/utils.ts`: + +```typescript +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} +``` + +**Step 6: components.json 생성** + +Create `frontend/components.json`: + +```json +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} +``` + +**Step 7: 빌드 테스트** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm run build +``` + +Expected: 빌드 성공 + +**Step 8: Commit** + +```bash +git add frontend/ +git commit -m "feat(frontend): initialize shadcn/ui configuration + +- Add tailwind.config.ts with shadcn/ui theme colors +- Update globals.css with CSS variables for dark/light mode +- Add utils.ts with cn() helper function +- Add components.json for shadcn/ui CLI" +``` + +--- + +## Task 2: next-themes 설치 및 ThemeProvider 설정 + +**Files:** +- Modify: `frontend/package.json` +- Create: `frontend/src/components/providers/theme-provider.tsx` +- Modify: `frontend/src/app/layout.tsx` + +**Step 1: next-themes 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm install next-themes +``` + +Expected: 패키지 설치 완료 + +**Step 2: ThemeProvider 컴포넌트 생성** + +Create `frontend/src/components/providers/theme-provider.tsx`: + +```typescript +"use client"; + +import * as React from "react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children}; +} +``` + +**Step 3: layout.tsx에 ThemeProvider 적용** + +Modify `frontend/src/app/layout.tsx`: + +```typescript +import type { Metadata } from 'next'; +import { Inter } from 'next/font/google'; +import './globals.css'; +import { ThemeProvider } from '@/components/providers/theme-provider'; + +const inter = Inter({ subsets: ['latin'] }); + +export const metadata: Metadata = { + title: 'Galaxy-PO', + description: 'Quant Portfolio Management Application', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + + ); +} +``` + +**Step 4: 빌드 테스트** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm run build +``` + +Expected: 빌드 성공 + +**Step 5: Commit** + +```bash +git add frontend/ +git commit -m "feat(frontend): add next-themes for dark/light mode + +- Create ThemeProvider component +- Apply ThemeProvider to root layout +- Enable system theme detection" +``` + +--- + +## Task 3: Lucide Icons 설치 + +**Files:** +- Modify: `frontend/package.json` + +**Step 1: lucide-react 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm install lucide-react +``` + +Expected: 패키지 설치 완료 + +**Step 2: Commit** + +```bash +git add frontend/package.json frontend/package-lock.json +git commit -m "feat(frontend): add lucide-react icons" +``` + +--- + +## Task 4: 핵심 shadcn/ui 컴포넌트 설치 + +**Files:** +- Create: `frontend/src/components/ui/button.tsx` +- Create: `frontend/src/components/ui/card.tsx` +- Create: `frontend/src/components/ui/dropdown-menu.tsx` +- Create: `frontend/src/components/ui/sheet.tsx` +- Create: `frontend/src/components/ui/tooltip.tsx` + +**Step 1: shadcn/ui CLI로 컴포넌트 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npx shadcn@latest add button card dropdown-menu sheet tooltip --yes +``` + +Expected: 컴포넌트 파일들이 `src/components/ui/`에 생성됨 + +**Step 2: 빌드 테스트** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm run build +``` + +Expected: 빌드 성공 + +**Step 3: Commit** + +```bash +git add frontend/ +git commit -m "feat(frontend): add core shadcn/ui components + +- button, card, dropdown-menu, sheet, tooltip" +``` + +--- + +## Task 5: 테마 토글 컴포넌트 생성 + +**Files:** +- Create: `frontend/src/components/ui/theme-toggle.tsx` + +**Step 1: ThemeToggle 컴포넌트 생성** + +Create `frontend/src/components/ui/theme-toggle.tsx`: + +```typescript +"use client"; + +import * as React from "react"; +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +export function ThemeToggle() { + const { setTheme } = useTheme(); + + return ( + + + + + + setTheme("light")}> + 라이트 + + setTheme("dark")}> + 다크 + + setTheme("system")}> + 시스템 + + + + ); +} +``` + +**Step 2: 빌드 테스트** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm run build +``` + +Expected: 빌드 성공 + +**Step 3: Commit** + +```bash +git add frontend/src/components/ui/theme-toggle.tsx +git commit -m "feat(frontend): add ThemeToggle component + +- Dropdown menu with light/dark/system options +- Uses Lucide icons for sun/moon" +``` + +--- + +## Task 6: 차트 라이브러리 설치 + +**Files:** +- Modify: `frontend/package.json` + +**Step 1: Recharts 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm install recharts +``` + +Expected: 패키지 설치 완료 + +**Step 2: TradingView Lightweight Charts 설치** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm install lightweight-charts +``` + +Expected: 패키지 설치 완료 + +**Step 3: Commit** + +```bash +git add frontend/package.json frontend/package-lock.json +git commit -m "feat(frontend): add chart libraries + +- recharts for general charts +- lightweight-charts for TradingView financial charts" +``` + +--- + +## Task 7: 테스트 페이지로 검증 + +**Files:** +- Modify: `frontend/src/app/page.tsx` (임시 수정 후 원복) + +**Step 1: 임시 테스트 페이지 생성** + +Temporarily modify `frontend/src/app/page.tsx` to test all components: + +```typescript +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { ThemeToggle } from "@/components/ui/theme-toggle"; +import { Home, Settings, User } from "lucide-react"; + +export default function TestPage() { + return ( +
+
+
+

Phase 1 테스트

+ +
+ + + + shadcn/ui 컴포넌트 테스트 + + +
+ + + + + +
+
+ + + + Lucide Icons +
+
+
+ + + + 테마 테스트 + + +

+ 위의 테마 토글 버튼으로 라이트/다크 모드를 전환해보세요. +

+
+
+
+
+ ); +} +``` + +**Step 2: 개발 서버 실행 및 확인** + +```bash +cd /home/zephyrdark/workspace/quant/galaxy-po/frontend +npm run dev +``` + +브라우저에서 http://localhost:3000 접속하여 확인: +- [ ] 버튼들이 올바르게 렌더링되는가 +- [ ] Lucide 아이콘이 표시되는가 +- [ ] 테마 토글이 작동하는가 (라이트/다크/시스템) +- [ ] 다크 모드에서 색상이 올바르게 변경되는가 + +**Step 3: 테스트 완료 후 원래 페이지로 복원하지 않음** + +(Phase 2에서 실제 레이아웃을 구현할 때 교체 예정) + +**Step 4: Commit** + +```bash +git add frontend/src/app/page.tsx +git commit -m "test(frontend): add Phase 1 test page + +- Verify shadcn/ui components render correctly +- Verify theme toggle works +- Verify Lucide icons display +- Temporary page for Phase 1 verification" +``` + +--- + +## 완료 체크리스트 + +- [ ] Task 1: shadcn/ui 초기화 및 설정 +- [ ] Task 2: next-themes 설치 및 ThemeProvider 설정 +- [ ] Task 3: Lucide Icons 설치 +- [ ] Task 4: 핵심 shadcn/ui 컴포넌트 설치 +- [ ] Task 5: 테마 토글 컴포넌트 생성 +- [ ] Task 6: 차트 라이브러리 설치 +- [ ] Task 7: 테스트 페이지로 검증 + +## 다음 단계 + +Phase 1 완료 후 Phase 2 (레이아웃 컴포넌트)로 진행: +- 새로운 Sidebar (접힘, 테마 토글, 사용자 메뉴) +- 새로운 Header (브레드크럼, 액션 버튼) +- 모바일 반응형 Sheet 메뉴 diff --git a/docs/plans/2026-02-05-phase2-layout.md b/docs/plans/2026-02-05-phase2-layout.md new file mode 100644 index 0000000..afcb4c1 --- /dev/null +++ b/docs/plans/2026-02-05-phase2-layout.md @@ -0,0 +1,450 @@ +# Phase 2: 레이아웃 컴포넌트 구현 계획 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** 접힘 가능한 사이드바, 새로운 헤더, 모바일 반응형 레이아웃을 구현한다. + +**Architecture:** App Router의 (dashboard) 라우트 그룹을 사용하여 인증된 페이지에 공통 레이아웃을 적용한다. + +**Tech Stack:** Next.js 16, React 19, shadcn/ui, Lucide Icons, next-themes + +--- + +## Task 1: 사이드바 컴포넌트 생성 + +**Files:** +- Create: `frontend/src/components/layout/new-sidebar.tsx` + +**Code:** +```typescript +"use client"; + +import * as React from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + LayoutDashboard, + Briefcase, + TrendingUp, + FlaskConical, + Database, + ChevronLeft, + ChevronRight, +} from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { ThemeToggle } from "@/components/ui/theme-toggle"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +const navItems = [ + { href: "/", label: "대시보드", icon: LayoutDashboard }, + { href: "/portfolio", label: "포트폴리오", icon: Briefcase }, + { href: "/strategy", label: "전략", icon: TrendingUp }, + { href: "/backtest", label: "백테스트", icon: FlaskConical }, + { href: "/admin/data", label: "데이터 관리", icon: Database }, +]; + +interface SidebarProps { + collapsed: boolean; + onToggle: () => void; +} + +export function Sidebar({ collapsed, onToggle }: SidebarProps) { + const pathname = usePathname(); + + return ( + + + + ); +} +``` + +**Commit:** `feat(frontend): add new Sidebar component with collapse support` + +--- + +## Task 2: 헤더 컴포넌트 생성 + +**Files:** +- Create: `frontend/src/components/layout/new-header.tsx` + +**Code:** +```typescript +"use client"; + +import * as React from "react"; +import { usePathname } from "next/navigation"; +import { Menu, LogOut } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { api } from "@/lib/api"; +import { useRouter } from "next/navigation"; + +const pageTitles: Record = { + "/": "대시보드", + "/portfolio": "포트폴리오", + "/portfolio/new": "새 포트폴리오", + "/strategy": "퀀트 전략", + "/strategy/multi-factor": "멀티팩터 전략", + "/strategy/quality": "슈퍼 퀄리티 전략", + "/strategy/value-momentum": "밸류-모멘텀 전략", + "/backtest": "백테스트", + "/admin/data": "데이터 관리", +}; + +interface HeaderProps { + onMenuClick?: () => void; + showMenuButton?: boolean; + username?: string; +} + +export function Header({ onMenuClick, showMenuButton, username }: HeaderProps) { + const pathname = usePathname(); + const router = useRouter(); + + const getPageTitle = () => { + if (pageTitles[pathname]) { + return pageTitles[pathname]; + } + if (pathname.startsWith("/portfolio/") && pathname.includes("/rebalance")) { + return "리밸런싱"; + } + if (pathname.startsWith("/portfolio/") && pathname.includes("/history")) { + return "히스토리"; + } + if (pathname.startsWith("/portfolio/")) { + return "포트폴리오 상세"; + } + if (pathname.startsWith("/backtest/")) { + return "백테스트 결과"; + } + return "Galaxy-PO"; + }; + + const handleLogout = () => { + api.logout(); + router.push("/login"); + }; + + return ( +
+
+ {showMenuButton && ( + + )} +

{getPageTitle()}

+
+ +
+ {username && ( + {username} + )} + +
+
+ ); +} +``` + +**Commit:** `feat(frontend): add new Header component` + +--- + +## Task 3: 대시보드 레이아웃 생성 + +**Files:** +- Create: `frontend/src/components/layout/dashboard-layout.tsx` + +**Code:** +```typescript +"use client"; + +import * as React from "react"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { Sidebar } from "@/components/layout/new-sidebar"; +import { Header } from "@/components/layout/new-header"; +import { Sheet, SheetContent } from "@/components/ui/sheet"; +import { api } from "@/lib/api"; + +interface DashboardLayoutProps { + children: React.ReactNode; +} + +export function DashboardLayout({ children }: DashboardLayoutProps) { + const [collapsed, setCollapsed] = useState(false); + const [mobileOpen, setMobileOpen] = useState(false); + const [username, setUsername] = useState(); + const [loading, setLoading] = useState(true); + const router = useRouter(); + + useEffect(() => { + const checkAuth = async () => { + try { + const user = await api.getCurrentUser(); + setUsername(user.username); + } catch { + router.push("/login"); + } finally { + setLoading(false); + } + }; + checkAuth(); + }, [router]); + + if (loading) { + return ( +
+
로딩 중...
+
+ ); + } + + return ( +
+ {/* Desktop Sidebar */} +
+ setCollapsed(!collapsed)} /> +
+ + {/* Mobile Sidebar */} + + + setMobileOpen(false)} /> + + + + {/* Main Content */} +
+
setMobileOpen(true)} + showMenuButton={true} + username={username} + /> +
{children}
+
+
+ ); +} +``` + +**Commit:** `feat(frontend): add DashboardLayout with responsive sidebar` + +--- + +## Task 4: 페이지에 레이아웃 적용 + +**Files:** +- Modify: `frontend/src/app/page.tsx` + +**Code:** +```typescript +"use client"; + +import { DashboardLayout } from "@/components/layout/dashboard-layout"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Wallet, TrendingUp, Briefcase, RefreshCw } from "lucide-react"; + +export default function DashboardPage() { + return ( + +
+

대시보드

+ + {/* Summary Cards */} +
+ + + 총 자산 + + + +
₩0
+

데이터 없음

+
+
+ + + + 총 수익률 + + + +
0%
+

데이터 없음

+
+
+ + + + 포트폴리오 + + + +
0개
+

포트폴리오 없음

+
+
+ + + + 리밸런싱 + + + +
0개
+

리밸런싱 필요 없음

+
+
+
+ + {/* Placeholder for charts */} +
+ + + 자산 추이 + + + 차트 영역 (Phase 3에서 구현) + + + + + + 섹터 배분 + + + 차트 영역 (Phase 3에서 구현) + + +
+
+
+ ); +} +``` + +**Commit:** `feat(frontend): apply DashboardLayout to main page` + +--- + +## Task 5: 포트폴리오 목록 페이지에 레이아웃 적용 + +**Files:** +- Modify: `frontend/src/app/portfolio/page.tsx` + +Read the existing file and wrap content with DashboardLayout, update styling to use shadcn/ui components. + +**Commit:** `feat(frontend): apply DashboardLayout to portfolio list page` + +--- + +## Task 6: 나머지 페이지에 레이아웃 적용 + +**Files to modify:** +- `frontend/src/app/portfolio/new/page.tsx` +- `frontend/src/app/portfolio/[id]/page.tsx` +- `frontend/src/app/strategy/page.tsx` +- `frontend/src/app/backtest/page.tsx` +- `frontend/src/app/admin/data/page.tsx` + +For each page, wrap existing content with DashboardLayout. + +**Commit:** `feat(frontend): apply DashboardLayout to all pages` + +--- + +## Task 7: 로그인 페이지 스타일 개선 + +**Files:** +- Modify: `frontend/src/app/login/page.tsx` + +Update to use shadcn/ui components (Card, Input, Button, Label). + +**Commit:** `feat(frontend): improve login page styling with shadcn/ui` + +--- + +## Task 8: 빌드 및 검증 + +Run build and verify all pages work correctly. + +**Commit:** Final cleanup if needed diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index e792ce2..0c499a7 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -7,7 +7,7 @@ import { Toaster } from '@/components/ui/sonner'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: 'Galaxy-PO', + title: 'Galaxis-Po', description: 'Quant Portfolio Management Application', }; diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 4fc87d0..d5608b9 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -34,7 +34,7 @@ export default function LoginPage() {
- Galaxy-PO + Galaxis-Po 포트폴리오 최적화 플랫폼에 오신 것을 환영합니다 diff --git a/frontend/src/components/layout/new-sidebar.tsx b/frontend/src/components/layout/new-sidebar.tsx index a297f8e..e023011 100644 --- a/frontend/src/components/layout/new-sidebar.tsx +++ b/frontend/src/components/layout/new-sidebar.tsx @@ -60,7 +60,7 @@ export function NewSidebar({ collapsed = false, onCollapsedChange }: NewSidebarP )}> {!isCollapsed && (
-

Galaxy-PO

+

Galaxis-Po

Quant Portfolio

)}