summaryrefslogtreecommitdiff
path: root/app/not-found.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/not-found.tsx')
-rw-r--r--app/not-found.tsx285
1 files changed, 285 insertions, 0 deletions
diff --git a/app/not-found.tsx b/app/not-found.tsx
new file mode 100644
index 00000000..b966d978
--- /dev/null
+++ b/app/not-found.tsx
@@ -0,0 +1,285 @@
+'use client'
+
+import { Button } from "@/components/ui/button"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
+import { Separator } from "@/components/ui/separator"
+import { ArrowLeft, Building2, Users, Info, AlertCircle, Copy } from "lucide-react"
+import Link from "next/link"
+import { useRouter } from "next/navigation"
+import { useState, useEffect } from "react"
+import { toast } from "sonner"
+
+// 에러 정보 타입
+interface ErrorInfo {
+ timestamp: string;
+ url: string;
+ userAgent: string;
+ platform: string;
+ language: string;
+ cookieEnabled: boolean;
+ onLine: boolean;
+ screen: {
+ width: number;
+ height: number;
+ colorDepth: number;
+ };
+ viewport: {
+ width: number;
+ height: number;
+ };
+ referrer: string;
+ localStorage: boolean;
+ sessionStorage: boolean;
+}
+
+export default function NotFound() {
+ const router = useRouter()
+ const [errorInfo, setErrorInfo] = useState<ErrorInfo | null>(null)
+
+ useEffect(() => {
+ // 브라우저 환경 정보 수집
+ const collectErrorInfo = () => {
+ const info: ErrorInfo = {
+ timestamp: new Date().toISOString(),
+ url: window.location.href,
+ userAgent: navigator.userAgent,
+ platform: navigator.platform,
+ language: navigator.language,
+ cookieEnabled: navigator.cookieEnabled,
+ onLine: navigator.onLine,
+ screen: {
+ width: screen.width,
+ height: screen.height,
+ colorDepth: screen.colorDepth,
+ },
+ viewport: {
+ width: window.innerWidth,
+ height: window.innerHeight,
+ },
+ referrer: document.referrer || '직접 접근',
+ localStorage: typeof Storage !== 'undefined',
+ sessionStorage: typeof Storage !== 'undefined',
+ }
+ setErrorInfo(info)
+ }
+
+ collectErrorInfo()
+ }, [])
+
+ const copyToClipboard = (text: string) => {
+ navigator.clipboard.writeText(text).then(() => {
+ toast.success("클립보드에 복사되었습니다")
+ }).catch(() => {
+ toast.error("복사에 실패했습니다")
+ })
+ }
+
+ const copyErrorInfo = () => {
+ if (!errorInfo) return
+
+ const errorText = `
+=== 404 에러 정보 ===
+시간: ${errorInfo.timestamp}
+URL: ${errorInfo.url}
+리퍼러: ${errorInfo.referrer}
+브라우저: ${errorInfo.userAgent}
+플랫폼: ${errorInfo.platform}
+언어: ${errorInfo.language}
+화면 해상도: ${errorInfo.screen.width}x${errorInfo.screen.height}
+뷰포트: ${errorInfo.viewport.width}x${errorInfo.viewport.height}
+온라인 상태: ${errorInfo.onLine ? '온라인' : '오프라인'}
+쿠키 활성화: ${errorInfo.cookieEnabled ? '예' : '아니오'}
+ `.trim()
+
+ copyToClipboard(errorText)
+ }
+
+ return (
+ <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 p-4">
+ <Card className="w-full max-w-2xl shadow-lg">
+ <CardHeader className="text-center space-y-4">
+ <div className="mx-auto w-24 h-24 bg-slate-100 dark:bg-slate-800 rounded-full flex items-center justify-center">
+ <span className="text-4xl font-bold text-slate-600 dark:text-slate-400">404</span>
+ </div>
+ <CardTitle className="text-3xl font-bold text-slate-900 dark:text-slate-100">
+ 이 페이지는 개발중이거나, 잘못된 URL 입니다.
+ </CardTitle>
+ <CardDescription className="text-lg text-slate-600 dark:text-slate-400">
+ THIS PAGE IS UNDER DEVELOPMENT OR INVALID URL.
+ </CardDescription>
+ </CardHeader>
+
+ <CardContent className="space-y-6">
+ <Separator />
+
+ <div className="text-center text-sm text-slate-500 dark:text-slate-400">
+ 아래 버튼을 통해 원하는 페이지로 이동하세요
+ </div>
+
+ <div className="grid gap-4 sm:grid-cols-3">
+ {/* SHI 사용자 홈 */}
+ <Button
+ asChild
+ variant="outline"
+ className="h-auto p-4 flex flex-col items-center space-y-2 hover:bg-blue-50 hover:border-blue-200 dark:hover:bg-blue-950"
+ >
+ <Link href="/ko/evcp/dashboard">
+ <Building2 className="h-6 w-6 text-blue-600 dark:text-blue-400" />
+ <span className="font-medium">SHI 사용자 홈</span>
+ <span className="text-xs text-slate-500 dark:text-slate-400">EVCP 대시보드</span>
+ </Link>
+ </Button>
+
+ {/* 벤더 홈 */}
+ <Button
+ asChild
+ variant="outline"
+ className="h-auto p-4 flex flex-col items-center space-y-2 hover:bg-green-50 hover:border-green-200 dark:hover:bg-green-950"
+ >
+ <Link href="/ko/partners">
+ <Users className="h-6 w-6 text-green-600 dark:text-green-400" />
+ <span className="font-medium">벤더 홈</span>
+ <span className="text-xs text-slate-500 dark:text-slate-400">협력업체 포털</span>
+ </Link>
+ </Button>
+
+ {/* 뒤로 가기 */}
+ <Button
+ onClick={() => router.back()}
+ variant="outline"
+ className="h-auto p-4 flex flex-col items-center space-y-2 hover:bg-slate-50 hover:border-slate-200 dark:hover:bg-slate-800"
+ >
+ <ArrowLeft className="h-6 w-6 text-slate-600 dark:text-slate-400" />
+ <span className="font-medium">뒤로 가기</span>
+ <span className="text-xs text-slate-500 dark:text-slate-400">이전 페이지로</span>
+ </Button>
+ </div>
+
+ <Separator />
+
+ <div className="flex justify-center gap-4">
+
+
+ {/* 에러 정보 다이얼로그 */}
+ <Dialog>
+ <DialogTrigger asChild>
+ <Button variant="ghost" size="sm">
+ <Info className="h-4 w-4 mr-2" />
+ 에러 정보
+ </Button>
+ </DialogTrigger>
+ <DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <AlertCircle className="h-5 w-5" />
+ 에러 상세 정보
+ </DialogTitle>
+ <DialogDescription>
+ 기술 지원을 위한 상세 환경 정보입니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ {errorInfo && (
+ <div className="space-y-4">
+ <div className="flex justify-end">
+ <Button onClick={copyErrorInfo} size="sm" variant="outline">
+ <Copy className="h-4 w-4 mr-2" />
+ 전체 복사
+ </Button>
+ </div>
+
+ <div className="space-y-3">
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">발생 시간</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.timestamp}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">요청 URL</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono break-all">
+ {errorInfo.url}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">이전 페이지</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono break-all">
+ {errorInfo.referrer}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">브라우저 정보</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono break-all">
+ {errorInfo.userAgent}
+ </div>
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">플랫폼</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.platform}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">언어</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.language}
+ </div>
+ </div>
+ </div>
+
+ <div className="grid grid-cols-2 gap-4">
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">화면 해상도</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.screen.width} × {errorInfo.screen.height}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">뷰포트 크기</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.viewport.width} × {errorInfo.viewport.height}
+ </div>
+ </div>
+ </div>
+
+ <div className="grid grid-cols-3 gap-4">
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">온라인 상태</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.onLine ? '온라인' : '오프라인'}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">쿠키 활성화</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.cookieEnabled ? '예' : '아니오'}
+ </div>
+ </div>
+
+ <div className="grid gap-2">
+ <label className="text-sm font-medium">색상 깊이</label>
+ <div className="p-2 bg-muted rounded text-sm font-mono">
+ {errorInfo.screen.colorDepth}bit
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
+ </DialogContent>
+ </Dialog>
+ </div>
+ </CardContent>
+ </Card>
+ </div>
+ )
+} \ No newline at end of file