인증 영역 개발 완료 (로그인, 회원가입, 프로필 관리)

- 로그인 페이지: 이메일 + 비밀번호 로그인, 소셜 로그인 버튼
- 회원가입 페이지: 3단계 funnel (계정정보, 개인정보, 사업장정보)
- 프로필 관리 페이지: 기본정보/매장정보 수정, 비밀번호 변경, 로그아웃
- MUI v6 + React Hook Form + Zod 검증
- Next.js 14 App Router, TypeScript 5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cherry2250
2025-10-24 11:27:15 +09:00
parent ca4dff559c
commit 4df7ba0697
23 changed files with 8921 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
import { Box, CircularProgress, Typography } from '@mui/material';
interface LoadingProps {
message?: string;
size?: number;
}
export function Loading({ message = '로딩 중...', size = 40 }: LoadingProps) {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
minHeight: '200px',
gap: 2,
}}
>
<CircularProgress size={size} />
{message && (
<Typography variant="body2" color="text.secondary">
{message}
</Typography>
)}
</Box>
);
}
+27
View File
@@ -0,0 +1,27 @@
'use client';
import { Snackbar, Alert } from '@mui/material';
import { useUIStore } from '@/stores/uiStore';
export function Toast() {
const { toast, hideToast } = useUIStore();
return (
<Snackbar
open={toast.open}
autoHideDuration={3000}
onClose={hideToast}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
sx={{ bottom: { xs: 80, sm: 24 } }}
>
<Alert
onClose={hideToast}
severity={toast.severity}
variant="filled"
sx={{ width: '100%' }}
>
{toast.message}
</Alert>
</Snackbar>
);
}
+54
View File
@@ -0,0 +1,54 @@
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { BottomNavigation, BottomNavigationAction, Paper } from '@mui/material';
import HomeIcon from '@mui/icons-material/Home';
import EventIcon from '@mui/icons-material/Event';
import BarChartIcon from '@mui/icons-material/BarChart';
import PersonIcon from '@mui/icons-material/Person';
export function BottomNav() {
const pathname = usePathname();
const router = useRouter();
const navItems = [
{ label: '홈', icon: <HomeIcon />, value: '/' },
{ label: '이벤트', icon: <EventIcon />, value: '/events' },
{ label: '분석', icon: <BarChartIcon />, value: '/analytics' },
{ label: '프로필', icon: <PersonIcon />, value: '/profile' },
];
const currentValue = navItems.find((item) => item.value === pathname)?.value || '/';
return (
<Paper
sx={{
position: 'fixed',
bottom: 0,
left: 0,
right: 0,
zIndex: 1100,
display: { xs: 'block', md: 'none' },
}}
elevation={3}
>
<BottomNavigation
value={currentValue}
onChange={(_, newValue) => {
router.push(newValue);
}}
showLabels
sx={{ height: 60 }}
>
{navItems.map((item) => (
<BottomNavigationAction
key={item.value}
label={item.label}
icon={item.icon}
value={item.value}
/>
))}
</BottomNavigation>
</Paper>
);
}