diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 19:03:21 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-28 19:03:21 +0000 |
| commit | 5036cf2908792cef45f06256e71f10920f647f49 (patch) | |
| tree | 3116e7419e872d45025d1d48e6ddaffe2ba2dd38 /app/not-found.tsx | |
| parent | 7ae037e9c2fc0be1fe68cecb461c5e1e837cb0da (diff) | |
(김준회) 기술영업 조선 RFQ (SHI/벤더)
Diffstat (limited to 'app/not-found.tsx')
| -rw-r--r-- | app/not-found.tsx | 285 |
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 |
