1
1
"use client" ;
2
2
3
+ import { Logo } from "@/components/logo" ;
4
+ import { Badge } from "@/components/ui/badge" ;
3
5
import { Button } from "@/components/ui/button" ;
4
6
import { Card , CardContent , CardDescription , CardFooter , CardHeader , CardTitle } from "@/components/ui/card" ;
5
- import { Badge } from "@/components/ui/badge" ;
6
- import {
7
- ERROR_NOT_FOUND ,
8
- ERROR_UNAUTHORIZED ,
9
- ERROR_USER_VERIFIED ,
10
- ERROR_NOT_IMPLEMENTED
11
- } from "@/lib/apollo" ;
7
+ import { ERROR_NOT_FOUND , ERROR_NOT_IMPLEMENTED , ERROR_UNAUTHORIZED , ERROR_USER_VERIFIED } from "@/lib/apollo" ;
12
8
import { ApolloError } from "@apollo/client" ;
13
- import {
14
- AlertCircle ,
15
- RefreshCw ,
16
- Home ,
17
- Lock ,
18
- Search ,
19
- WifiOff ,
20
- Code ,
21
- Shield
22
- } from "lucide-react" ;
9
+ import { AlertCircle , Code , Home , Lock , RefreshCw , Search , Shield , WifiOff } from "lucide-react" ;
23
10
import Link from "next/link" ;
24
- import { Logo } from "@/components/logo" ;
25
11
import { useEffect } from "react" ;
26
12
27
13
interface GlobalErrorProps {
@@ -33,32 +19,32 @@ function getErrorInfo(error: Error | ApolloError) {
33
19
// Check if it's an ApolloError
34
20
if ( error instanceof ApolloError ) {
35
21
// Network errors
36
- if ( error . networkError && ' statusCode' in error . networkError ) {
22
+ if ( error . networkError && " statusCode" in error . networkError ) {
37
23
switch ( error . networkError . statusCode ) {
38
24
case 401 :
39
25
return {
40
26
title : "未經授權" ,
41
27
description : "您的登入狀態已過期,請重新登入。" ,
42
28
icon : Lock ,
43
- actionHref : "/login"
29
+ actionHref : "/login" ,
44
30
} ;
45
31
case 403 :
46
32
return {
47
- title : "權限不足" ,
33
+ title : "權限不足" ,
48
34
description : "您沒有權限執行此操作。" ,
49
- icon : Shield
35
+ icon : Shield ,
50
36
} ;
51
37
case 404 :
52
38
return {
53
39
title : "找不到資源" ,
54
- description : "請求的資源不存在或已被移除。" ,
55
- icon : Search
40
+ description : "請求的資源不存在或已被移除。" ,
41
+ icon : Search ,
56
42
} ;
57
43
case 500 :
58
44
return {
59
45
title : "伺服器錯誤" ,
60
46
description : "伺服器發生內部錯誤,請稍後再試。" ,
61
- icon : AlertCircle
47
+ icon : AlertCircle ,
62
48
} ;
63
49
}
64
50
}
@@ -67,7 +53,7 @@ function getErrorInfo(error: Error | ApolloError) {
67
53
return {
68
54
title : "網路連線錯誤" ,
69
55
description : "無法連接到伺服器,請檢查網路連線。" ,
70
- icon : WifiOff
56
+ icon : WifiOff ,
71
57
} ;
72
58
}
73
59
@@ -81,33 +67,33 @@ function getErrorInfo(error: Error | ApolloError) {
81
67
return {
82
68
title : "找不到資料" ,
83
69
description : "請求的資料不存在或已被刪除。" ,
84
- icon : Search
70
+ icon : Search ,
85
71
} ;
86
72
case ERROR_UNAUTHORIZED :
87
73
return {
88
- title : "未經授權" ,
74
+ title : "未經授權" ,
89
75
description : "請登入後再試,或您的權限不足。" ,
90
76
icon : Lock ,
91
- actionHref : "/login"
77
+ actionHref : "/login" ,
92
78
} ;
93
79
case ERROR_USER_VERIFIED :
94
80
return {
95
81
title : "帳號已驗證" ,
96
82
description : "此帳號已經完成驗證程序。" ,
97
- icon : Shield
83
+ icon : Shield ,
98
84
} ;
99
85
case ERROR_NOT_IMPLEMENTED :
100
86
return {
101
87
title : "功能未實作" ,
102
88
description : "此功能目前尚未實作,請稍後再試。" ,
103
- icon : Code
89
+ icon : Code ,
104
90
} ;
105
91
}
106
92
107
93
return {
108
94
title : "GraphQL 查詢錯誤" ,
109
95
description : firstError . message || "GraphQL 查詢發生錯誤。" ,
110
- icon : AlertCircle
96
+ icon : AlertCircle ,
111
97
} ;
112
98
}
113
99
}
@@ -116,7 +102,7 @@ function getErrorInfo(error: Error | ApolloError) {
116
102
return {
117
103
title : "應用程式發生錯誤" ,
118
104
description : error . message || "應用程式遇到預期外的錯誤。" ,
119
- icon : AlertCircle
105
+ icon : AlertCircle ,
120
106
} ;
121
107
}
122
108
@@ -152,91 +138,141 @@ export default function GlobalError({ error, reset }: GlobalErrorProps) {
152
138
</ div >
153
139
Database Playground
154
140
</ Link >
155
-
156
- < Card className = "min-w-md max-w-2xl" >
157
- < CardHeader className = "flex w-full flex-col items-center text-center" >
141
+
142
+ < Card className = "max-w-2xl min-w-md" >
143
+ < CardHeader
144
+ className = { `flex w-full flex-col items-center text-center` }
145
+ >
158
146
< errorInfo . icon className = "mb-2 size-7 text-red-500" />
159
147
< CardTitle className = "text-xl" > { errorInfo . title } </ CardTitle >
160
148
< CardDescription >
161
149
{ errorInfo . description }
162
150
</ CardDescription >
163
151
</ CardHeader >
164
-
152
+
165
153
< CardContent className = "flex flex-col items-center gap-4" >
166
- < div className = "w-full rounded-md bg-red-50 p-4 text-left" >
167
- < details className = "text-sm" >
168
- < summary className = "cursor-pointer font-medium text-red-800" >
169
- 錯誤詳細資訊
170
- </ summary >
171
- < div className = "text-red-700 space-y-2 mt-2" >
172
- < p className = "font-medium" > { error . name } : { error . message } </ p >
173
-
174
- { error . stack && (
175
- < pre className = "whitespace-pre-wrap text-xs bg-red-100 p-2 rounded overflow-x-auto" >
154
+ < div className = "w-full rounded-md bg-red-50 p-4 text-left" >
155
+ < details className = "text-sm" >
156
+ < summary className = "cursor-pointer font-medium text-red-800" >
157
+ 錯誤詳細資訊
158
+ </ summary >
159
+ < div className = "mt-2 space-y-2 text-red-700" >
160
+ < p className = "font-medium" > { error . name } : { error . message } </ p >
161
+
162
+ { error . stack && (
163
+ < pre
164
+ className = { `
165
+ overflow-x-auto rounded bg-red-100 p-2 text-xs
166
+ whitespace-pre-wrap
167
+ ` }
168
+ >
176
169
{ error . stack }
177
- </ pre >
178
- ) }
179
-
180
- { error instanceof ApolloError && (
181
- < div className = "space-y-2" >
182
- { error . networkError && (
183
- < div >
184
- < Badge variant = "destructive" className = "text-xs mb-1" > Network Error</ Badge >
185
- < pre className = "whitespace-pre-wrap text-xs bg-red-100 p-2 rounded" >
170
+ </ pre >
171
+ ) }
172
+
173
+ { error instanceof ApolloError && (
174
+ < div className = "space-y-2" >
175
+ { error . networkError && (
176
+ < div >
177
+ < Badge
178
+ variant = "destructive"
179
+ className = { `mb-1 text-xs` }
180
+ >
181
+ Network Error
182
+ </ Badge >
183
+ < pre
184
+ className = { `
185
+ rounded bg-red-100 p-2 text-xs
186
+ whitespace-pre-wrap
187
+ ` }
188
+ >
186
189
{ JSON . stringify ( error . networkError , null , 2 ) }
187
- </ pre >
188
- </ div >
189
- ) }
190
-
191
- { error . graphQLErrors && error . graphQLErrors . length > 0 && (
192
- < div >
193
- < Badge variant = "destructive" className = "text-xs mb-1" >
194
- GraphQL Errors ({ error . graphQLErrors . length } )
195
- </ Badge >
196
- < pre className = "whitespace-pre-wrap text-xs bg-red-100 p-2 rounded" >
190
+ </ pre >
191
+ </ div >
192
+ ) }
193
+
194
+ { error . graphQLErrors && error . graphQLErrors . length > 0 && (
195
+ < div >
196
+ < Badge
197
+ variant = "destructive"
198
+ className = { `mb-1 text-xs` }
199
+ >
200
+ GraphQL Errors ({ error . graphQLErrors . length } )
201
+ </ Badge >
202
+ < pre
203
+ className = { `
204
+ rounded bg-red-100 p-2 text-xs
205
+ whitespace-pre-wrap
206
+ ` }
207
+ >
197
208
{ JSON . stringify ( error . graphQLErrors , null , 2 ) }
198
- </ pre >
199
- </ div >
200
- ) }
201
- </ div >
202
- ) }
203
- </ div >
204
- </ details >
205
- </ div >
206
-
207
- < div className = "flex flex-col sm:flex-row gap-3" >
208
- < Button onClick = { reset } variant = "default" className = "flex items-center gap-2" >
209
+ </ pre >
210
+ </ div >
211
+ ) }
212
+ </ div >
213
+ ) }
214
+ </ div >
215
+ </ details >
216
+ </ div >
217
+
218
+ < div
219
+ className = { `
220
+ flex flex-col gap-3
221
+ sm:flex-row
222
+ ` }
223
+ >
224
+ < Button
225
+ onClick = { reset }
226
+ variant = "default"
227
+ className = { `flex items-center gap-2` }
228
+ >
209
229
< RefreshCw className = "size-4" />
210
230
重試
211
231
</ Button >
212
-
213
- { errorInfo . actionHref ? (
214
- < Button asChild variant = "outline" className = "flex items-center gap-2" >
215
- < Link href = { errorInfo . actionHref } >
216
- 前往處理
217
- </ Link >
218
- </ Button >
219
- ) : (
220
- < Button asChild variant = "outline" className = "flex items-center gap-2" >
221
- < Link href = "/" >
222
- < Home className = "size-4" />
223
- 回到首頁
224
- </ Link >
225
- </ Button >
226
- ) }
232
+
233
+ { errorInfo . actionHref
234
+ ? (
235
+ < Button
236
+ asChild
237
+ variant = "outline"
238
+ className = { `flex items-center gap-2` }
239
+ >
240
+ < Link href = { errorInfo . actionHref } >
241
+ 前往處理
242
+ </ Link >
243
+ </ Button >
244
+ )
245
+ : (
246
+ < Button
247
+ asChild
248
+ variant = "outline"
249
+ className = { `flex items-center gap-2` }
250
+ >
251
+ < Link href = "/" >
252
+ < Home className = "size-4" />
253
+ 回到首頁
254
+ </ Link >
255
+ </ Button >
256
+ ) }
227
257
</ div >
228
258
</ CardContent >
229
-
259
+
230
260
< CardFooter
231
- className = { `justify-center text-center text-xs text-muted-foreground` }
261
+ className = { `
262
+ justify-center text-center text-xs text-muted-foreground
263
+ ` }
232
264
>
233
265
< section className = "flex flex-col items-center gap-1" >
234
266
< p > 如果問題持續發生,請聯絡開發者進行處理。</ p >
235
267
< p className = "text-red-600" >
236
- 錯誤時間:{ new Date ( ) . toLocaleString ( ' zh-TW' , { timeZone : ' Asia/Taipei' } ) }
268
+ 錯誤時間:{ new Date ( ) . toLocaleString ( " zh-TW" , { timeZone : " Asia/Taipei" } ) }
237
269
</ p >
238
- { 'digest' in error && error . digest && (
239
- < p className = "text-red-600" > 錯誤 ID:{ error . digest } </ p >
270
+ { "digest" in error && error . digest && (
271
+ < p
272
+ className = { `text-red-600` }
273
+ >
274
+ 錯誤 ID:{ error . digest }
275
+ </ p >
240
276
) }
241
277
</ section >
242
278
</ CardFooter >
0 commit comments