diff --git a/CLAUDE.md b/CLAUDE.md index 06c1bff..d09ce9d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,10 @@ ### 기술 스택 - **API Gateway**: Apache APISIX 3.8.0 -- **Frontend**: React 18 + Vite + TypeScript + shadcn/ui + Tailwind CSS +- **Frontend**: React 18 + Vite + TypeScript + Material-UI (MUI) + - 이전: Lucide React + Tailwind CSS + - 현재: Material-UI (@mui/material, @emotion/react, @emotion/styled, @mui/icons-material) + - 디자인: Google OAuth 스타일 UI/UX - **Backend**: Python 3.11 + FastAPI + Motor (MongoDB async) - **Database**: MongoDB 7.0 - **Cache/Queue**: Redis 7 diff --git a/oauth/frontend/src/pages/Dashboard.tsx b/oauth/frontend/src/pages/Dashboard.tsx index b9e3811..ed647b8 100644 --- a/oauth/frontend/src/pages/Dashboard.tsx +++ b/oauth/frontend/src/pages/Dashboard.tsx @@ -1,31 +1,82 @@ import { useState } from 'react' +import { useNavigate } from 'react-router-dom' import { useAuth } from '../contexts/AuthContext' -import { - LayoutDashboard, - Users, - Settings, - LogOut, - ChevronDown, - Bell, - Search, +import { + Box, + Container, + Grid, + Card, + CardContent, + Typography, + Button, + AppBar, + Toolbar, + IconButton, + Avatar, Menu, - X, - Shield, - Key, - Activity, - Clock -} from 'lucide-react' + MenuItem, + Drawer, + List, + ListItem, + ListItemIcon, + ListItemText, + ListItemButton, + Divider, + Paper, + LinearProgress, + Chip, + InputBase, + Badge +} from '@mui/material' +import { + Dashboard as DashboardIcon, + Apps, + Person, + AdminPanelSettings, + Menu as MenuIcon, + Logout, + Settings, + TrendingUp, + Security, + Speed, + AccessTime, + Notifications, + Search, + VpnKey, + Timeline +} from '@mui/icons-material' const Dashboard = () => { + const [drawerOpen, setDrawerOpen] = useState(false) + const [anchorEl, setAnchorEl] = useState(null) const { user, logout } = useAuth() - const [isSidebarOpen, setIsSidebarOpen] = useState(true) - const [isProfileOpen, setIsProfileOpen] = useState(false) + const navigate = useNavigate() + + const handleLogout = async () => { + await logout() + navigate('/login') + } + + const handleProfileMenuOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + + const handleProfileMenuClose = () => { + setAnchorEl(null) + } + + const menuItems = [ + { text: '대시보드', icon: , path: '/dashboard' }, + { text: '애플리케이션', icon: , path: '/applications' }, + { text: '프로필 설정', icon: , path: '/profile' }, + { text: '관리자 패널', icon: , path: '/admin', adminOnly: true }, + ] const stats = [ - { title: '활성 세션', value: '24', icon: Users, change: '+12%' }, - { title: '등록된 앱', value: '8', icon: Key, change: '+2' }, - { title: '이번 달 로그인', value: '1,429', icon: Activity, change: '+48%' }, - { title: '평균 응답시간', value: '132ms', icon: Clock, change: '-12%' }, + { title: '활성 세션', value: '24', icon: , change: '+12%', color: '#1a73e8' }, + { title: '등록된 앱', value: '8', icon: , change: '+2', color: '#34a853' }, + { title: '이번 달 로그인', value: '1,429', icon: , change: '+48%', color: '#fbbc04' }, + { title: '평균 응답시간', value: '132ms', icon: , change: '-12%', color: '#ea4335' }, ] const recentActivities = [ @@ -36,154 +87,209 @@ const Dashboard = () => { ] return ( -
- {/* Sidebar */} - + + + + OAuth System + + + {/* Search Bar */} + + + + + + + + + + + + + + + + {user?.name?.[0] || user?.email?.[0]?.toUpperCase() || 'U'} + + + + { handleProfileMenuClose(); navigate('/profile'); }}> + + 프로필 설정 + + + + + 로그아웃 + + + + + + {/* Drawer */} + setDrawerOpen(false)} + > + + + + OAuth System + + + + {menuItems.map((item) => { + if (item.adminOnly && user?.role !== 'system_admin') return null + return ( + { + navigate(item.path) + setDrawerOpen(false) + }} + selected={location.pathname === item.path} + sx={{ + '&.Mui-selected': { + backgroundColor: '#e8f0fe', + color: '#1967d2', + '& .MuiListItemIcon-root': { + color: '#1967d2', + } + } + }} + > + {item.icon} + + + ) + })} + + + + + + + + + + {/* Main Content */} -
- {/* Header */} -
-
-
- -
- - -
-
- -
- - -
- - - {isProfileOpen && ( -
- - 프로필 설정 - -
- -
- )} -
-
-
-
- - {/* Dashboard Content */} -
-
-

대시보드

-

OAuth 시스템 현황을 한눈에 확인하세요

-
+ + + {/* Page Header */} + + + 대시보드 + + + OAuth 시스템 현황을 한눈에 확인하세요 + + {/* Stats Grid */} -
+ {stats.map((stat) => ( -
-
-
- -
- - {stat.change} - -
-

{stat.value}

-

{stat.title}

-
+ + + + + + {stat.icon} + + + + + {stat.value} + + + {stat.title} + + + + ))} -
+ - {/* Recent Activities */} -
-
-

최근 활동

-
-
-
- {recentActivities.map((activity) => ( -
-
-

{activity.action}

-

{activity.app}

-
- {activity.time} -
- ))} -
-
-
-
-
-
+ {/* Recent Activity */} + + + + 최근 활동 + + + + {recentActivities.map((activity, index) => ( + + + + {activity.action} + + } + secondary={ + + + {activity.app} + + + • {activity.time} + + + } + /> + + {index < recentActivities.length - 1 && } + + ))} + + + + + ) } diff --git a/oauth/frontend/src/pages/SignupPage.tsx b/oauth/frontend/src/pages/SignupPage.tsx index 35f93f0..9473c06 100644 --- a/oauth/frontend/src/pages/SignupPage.tsx +++ b/oauth/frontend/src/pages/SignupPage.tsx @@ -1,17 +1,30 @@ import { useState } from 'react' import { useNavigate } from 'react-router-dom' -import { - Eye, - EyeOff, - Loader2, - Shield, +import { + Box, + Container, + TextField, + Button, + Typography, + Link, + Paper, + IconButton, + InputAdornment, + Checkbox, + FormControlLabel, + LinearProgress, + Alert, + Stack +} from '@mui/material' +import { + Visibility, + VisibilityOff, + Person, + Email, Lock, - Mail, - ArrowRight, - User, - Building, - Check -} from 'lucide-react' + Business, + ArrowForward +} from '@mui/icons-material' const SignupPage = () => { const [formData, setFormData] = useState({ @@ -25,7 +38,6 @@ const SignupPage = () => { const [showPassword, setShowPassword] = useState(false) const [showConfirmPassword, setShowConfirmPassword] = useState(false) const [isLoading, setIsLoading] = useState(false) - const [focusedInput, setFocusedInput] = useState(null) const [passwordStrength, setPasswordStrength] = useState(0) const navigate = useNavigate() @@ -89,12 +101,12 @@ const SignupPage = () => { const getPasswordStrengthColor = () => { switch(passwordStrength) { - case 0: return 'bg-gray-400' - case 1: return 'bg-red-500' - case 2: return 'bg-yellow-500' - case 3: return 'bg-blue-500' - case 4: return 'bg-green-500' - default: return 'bg-gray-400' + case 0: return '#bdbdbd' + case 1: return '#f44336' + case 2: return '#ff9800' + case 3: return '#2196f3' + case 4: return '#4caf50' + default: return '#bdbdbd' } } @@ -110,293 +122,293 @@ const SignupPage = () => { } return ( -
- {/* Animated background elements */} -
-
-
-
-
+ + + + {/* Logo and Title */} + + + AiMond + + + AiMond 계정 만들기 + + + 계속하려면 AiMond로 이동 + + - {/* Grid pattern overlay */} -
- - {/* Main content */} -
-
- - {/* Left side - Branding */} -
-
-
-
- -
-

- AiMond Authorization -

-
-

- 새로운 계정을 생성하고 시작하세요 -

-
- -
-
-
- -
-
-

빠른 가입 절차

-

몇 가지 정보만으로 간단하게 가입 완료

-
-
- -
-
- -
-
-

엔터프라이즈 보안

-

최고 수준의 보안으로 데이터 보호

-
-
- -
-
- -
-
-

조직 단위 관리

-

팀과 조직을 효율적으로 관리

-
-
-
-
- - {/* Right side - Signup Form */} -
-
- {/* Form Header */} -
-

Create Account

-

새로운 계정을 생성하세요

-
- - {/* Signup Form */} -
- {/* Name Input */} -
- -
- setFocusedInput('name')} - onBlur={() => setFocusedInput(null)} - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-purple-400 focus:bg-white/10 transition-all duration-300" - placeholder="홍길동" - disabled={isLoading} - /> -
-
-
- - {/* Email Input */} -
- -
- setFocusedInput('email')} - onBlur={() => setFocusedInput(null)} - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-purple-400 focus:bg-white/10 transition-all duration-300" - placeholder="your@email.com" - disabled={isLoading} - /> -
-
-
- - {/* Organization Input */} -
- -
- setFocusedInput('organization')} - onBlur={() => setFocusedInput(null)} - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-purple-400 focus:bg-white/10 transition-all duration-300" - placeholder="AiMond Inc." - disabled={isLoading} - /> -
-
-
- - {/* Password Input */} -
- -
- setFocusedInput('password')} - onBlur={() => setFocusedInput(null)} - className="w-full px-4 py-3 pr-12 bg-white/5 border border-white/10 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-purple-400 focus:bg-white/10 transition-all duration-300" - placeholder="••••••••" - disabled={isLoading} - /> - -
-
- {formData.password && ( -
-
-
-
- {getPasswordStrengthText()} -
- )} -
- - {/* Confirm Password Input */} -
- -
- setFocusedInput('confirmPassword')} - onBlur={() => setFocusedInput(null)} - className="w-full px-4 py-3 pr-12 bg-white/5 border border-white/10 rounded-xl text-white placeholder-gray-400 focus:outline-none focus:border-purple-400 focus:bg-white/10 transition-all duration-300" - placeholder="••••••••" - disabled={isLoading} - /> - -
-
-
- - {/* Terms Agreement */} -
- + + + + + + + ), + }} /> - -
+ - {/* Submit Button */} - -
+ + + + ), + }} + /> - {/* Sign in link */} -

- 이미 계정이 있으신가요?{' '} - - 로그인하기 - -

-
+ + + + ), + }} + /> - {/* Security Badge */} -
- - 256-bit SSL 암호화로 보호됨 -
-
-
-
-
+ + + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge="end" + size="small" + > + {showPassword ? : } + + + ), + }} + /> + + {formData.password && ( + + + + 비밀번호 강도: {getPasswordStrengthText()} + + + )} + + + + + ), + endAdornment: ( + + setShowConfirmPassword(!showConfirmPassword)} + edge="end" + size="small" + > + {showConfirmPassword ? : } + + + ), + }} + /> + + + 문자, 숫자, 기호를 조합하여 8자 이상의 비밀번호를 사용하세요 + + + + } + label={ + + 서비스 약관 및{' '} + 개인정보처리방침에 동의합니다 + + } + /> + + + + 대신 로그인 + + + + + + + + {/* Footer Text */} + + + 하나의 AiMond 계정으로 모든 AiMond 서비스를 이용하실 수 있습니다 + + + + + + {/* Footer */} + + + + 한국어 + + + 도움말 + + + 개인정보처리방침 + + + 약관 + + + + + + ) }