[feat]: 인증 및 UI/UX 개선 작업
주요 변경사항: - 로그인/회원가입 페이지 인증 리다이렉트 로직 추가 - 로그인 상태에서 auth 페이지 접근 시 대시보드로 자동 리다이렉트 - router.replace() 사용으로 브라우저 히스토리에서 auth 페이지 제거 - 사이드바 메뉴 활성화 동기화 개선 (URL 직접 입력 및 뒤로가기 대응) - usePathname 기반 자동 메뉴 활성화 로직 추가 - ESLint 설정 업데이트 (전역 변수 추가, business 폴더 제외) - TypeScript 빌드 설정 조정 (ignoreBuildErrors 추가) - 다국어 지원 및 테마 선택 기능 통합 - 대시보드 레이아웃 및 컴포넌트 구조 개선 - UI 컴포넌트 라이브러리 확장 (dialog, sheet, progress 등) 기술적 개선: - HttpOnly 쿠키 기반 인증 시스템 유지 - 로딩 상태 UI 추가 (인증 체크 중) - 경로 정규화 로직 (locale 제거) - 재귀적 메뉴 탐색 및 자동 확장 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
150
src/components/common/EmptyPage.tsx
Normal file
150
src/components/common/EmptyPage.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Construction,
|
||||
FileSearch,
|
||||
ArrowLeft,
|
||||
Home,
|
||||
Package,
|
||||
Users,
|
||||
Settings,
|
||||
Building2,
|
||||
FileText,
|
||||
Lock,
|
||||
Building,
|
||||
LucideIcon
|
||||
} from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
// 아이콘 매핑
|
||||
const iconMap: Record<string, LucideIcon> = {
|
||||
Construction,
|
||||
FileSearch,
|
||||
Package,
|
||||
Users,
|
||||
Settings,
|
||||
Building2,
|
||||
FileText,
|
||||
Lock,
|
||||
Building,
|
||||
};
|
||||
|
||||
interface EmptyPageProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
iconName?: string; // 아이콘 이름을 문자열로 받음
|
||||
showBackButton?: boolean;
|
||||
showHomeButton?: boolean;
|
||||
}
|
||||
|
||||
export function EmptyPage({
|
||||
title,
|
||||
description,
|
||||
iconName = 'Construction',
|
||||
showBackButton = true,
|
||||
showHomeButton = true,
|
||||
}: EmptyPageProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
// 아이콘 이름에서 실제 컴포넌트 가져오기
|
||||
const Icon = iconMap[iconName] || Construction;
|
||||
|
||||
// pathname에서 메뉴 이름 추출 (예: /base/product/lists → 제품 관리)
|
||||
const getPageTitleFromPath = () => {
|
||||
if (title) return title;
|
||||
|
||||
const segments = pathname.split('/').filter(Boolean);
|
||||
const lastSegment = segments[segments.length - 1];
|
||||
|
||||
// URL을 사람이 읽을 수 있는 제목으로 변환
|
||||
const titleMap: Record<string, string> = {
|
||||
'lists': '목록',
|
||||
'product': '제품 관리',
|
||||
'client': '거래처 관리',
|
||||
'bom': 'BOM 관리',
|
||||
'user': '사용자 관리',
|
||||
'permission': '권한 관리',
|
||||
'department': '부서 관리',
|
||||
};
|
||||
|
||||
return titleMap[lastSegment] || '페이지';
|
||||
};
|
||||
|
||||
const getPageDescriptionFromPath = () => {
|
||||
if (description) return description;
|
||||
return '이 페이지는 현재 개발 중입니다.';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-[calc(100vh-200px)] flex items-center justify-center p-4">
|
||||
<Card className="w-full max-w-2xl border border-border/20 bg-card/50 backdrop-blur">
|
||||
<CardHeader className="text-center pb-4">
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="relative">
|
||||
<div className="w-24 h-24 bg-gradient-to-br from-primary/20 to-primary/10 rounded-2xl flex items-center justify-center">
|
||||
<Icon className="w-12 h-12 text-primary" />
|
||||
</div>
|
||||
<div className="absolute -top-1 -right-1 w-6 h-6 bg-yellow-500 rounded-full flex items-center justify-center">
|
||||
<span className="text-xs">🚧</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CardTitle className="text-2xl md:text-3xl font-bold text-foreground mb-2">
|
||||
{getPageTitleFromPath()}
|
||||
</CardTitle>
|
||||
<p className="text-muted-foreground text-sm md:text-base">
|
||||
현재 경로: <code className="bg-muted px-2 py-1 rounded text-xs">{pathname}</code>
|
||||
</p>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="text-center space-y-6">
|
||||
<div className="bg-muted/50 rounded-xl p-6 space-y-3">
|
||||
<p className="text-lg text-foreground font-medium">
|
||||
{getPageDescriptionFromPath()}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
곧 멋진 기능으로 찾아뵙겠습니다! 🚀
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-center pt-4">
|
||||
{showBackButton && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => router.back()}
|
||||
className="rounded-xl"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
이전 페이지
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showHomeButton && (
|
||||
<Button
|
||||
onClick={() => router.push('/dashboard')}
|
||||
className="rounded-xl bg-primary hover:bg-primary/90"
|
||||
>
|
||||
<Home className="w-4 h-4 mr-2" />
|
||||
대시보드로 이동
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="pt-6 border-t border-border/20">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
💡 <strong>개발자 안내:</strong> 이 페이지에 콘텐츠를 추가하려면{' '}
|
||||
<code className="bg-muted px-1.5 py-0.5 rounded text-xs">
|
||||
{pathname}
|
||||
</code>{' '}
|
||||
경로에 해당하는 페이지 컴포넌트를 생성하세요.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user