1- import { useEffect , useCallback , useState } from 'react' ;
2- import { createPortal } from 'react-dom' ;
1+ import { useEffect , useCallback , useState } from 'react'
2+ import { createPortal } from 'react-dom'
33import {
44 Snowflake ,
55 Clock ,
@@ -12,23 +12,32 @@ import {
1212 X ,
1313 Thermometer ,
1414 Calendar ,
15- Activity
16- } from 'lucide-react' ;
17- import type { Cooldown , CooldownReason } from '@/lib/transport/types' ;
18- import { useCooldowns } from '@/hooks/use-cooldowns' ;
15+ Activity ,
16+ } from 'lucide-react'
17+ import type { Cooldown , CooldownReason } from '@/lib/transport/types'
18+ import { useCooldowns } from '@/hooks/use-cooldowns'
1919
2020interface CooldownDetailsDialogProps {
21- cooldown : Cooldown | null ;
22- open : boolean ;
23- onOpenChange : ( open : boolean ) => void ;
24- onClear : ( ) => void ;
25- isClearing : boolean ;
26- onDisable : ( ) => void ;
27- isDisabling : boolean ;
21+ cooldown : Cooldown | null
22+ open : boolean
23+ onOpenChange : ( open : boolean ) => void
24+ onClear : ( ) => void
25+ isClearing : boolean
26+ onDisable : ( ) => void
27+ isDisabling : boolean
2828}
2929
3030// Reason 中文说明和图标
31- const REASON_INFO : Record < CooldownReason , { label : string ; description : string ; icon : typeof Server ; color : string ; bgColor : string } > = {
31+ const REASON_INFO : Record <
32+ CooldownReason ,
33+ {
34+ label : string
35+ description : string
36+ icon : typeof Server
37+ color : string
38+ bgColor : string
39+ }
40+ > = {
3241 server_error : {
3342 label : '服务器错误' ,
3443 description : '上游服务器返回 5xx 错误,系统自动进入冷却保护' ,
@@ -71,7 +80,7 @@ const REASON_INFO: Record<CooldownReason, { label: string; description: string;
7180 color : 'text-text-muted' ,
7281 bgColor : 'bg-surface-secondary border-border' ,
7382 } ,
74- } ;
83+ }
7584
7685export function CooldownDetailsDialog ( {
7786 cooldown,
@@ -83,66 +92,69 @@ export function CooldownDetailsDialog({
8392 isDisabling,
8493} : CooldownDetailsDialogProps ) {
8594 // 获取 formatRemaining 函数用于实时倒计时
86- const { formatRemaining } = useCooldowns ( ) ;
95+ const { formatRemaining } = useCooldowns ( )
8796
8897 // 实时倒计时状态
89- const [ liveCountdown , setLiveCountdown ] = useState < string > ( '' ) ;
98+ const [ liveCountdown , setLiveCountdown ] = useState < string > ( '' )
9099
91100 // Handle ESC key
92- const handleKeyDown = useCallback ( ( e : KeyboardEvent ) => {
93- if ( e . key === 'Escape' ) {
94- onOpenChange ( false ) ;
95- }
96- } , [ onOpenChange ] ) ;
101+ const handleKeyDown = useCallback (
102+ ( e : KeyboardEvent ) => {
103+ if ( e . key === 'Escape' ) {
104+ onOpenChange ( false )
105+ }
106+ } ,
107+ [ onOpenChange ]
108+ )
97109
98110 useEffect ( ( ) => {
99111 if ( open ) {
100- document . addEventListener ( 'keydown' , handleKeyDown ) ;
101- document . body . style . overflow = 'hidden' ;
112+ document . addEventListener ( 'keydown' , handleKeyDown )
113+ document . body . style . overflow = 'hidden'
102114 return ( ) => {
103- document . removeEventListener ( 'keydown' , handleKeyDown ) ;
104- document . body . style . overflow = '' ;
105- } ;
115+ document . removeEventListener ( 'keydown' , handleKeyDown )
116+ document . body . style . overflow = ''
117+ }
106118 }
107- } , [ open , handleKeyDown ] ) ;
119+ } , [ open , handleKeyDown ] )
108120
109121 // 每秒更新倒计时
110122 useEffect ( ( ) => {
111123 if ( ! cooldown ) {
112- setLiveCountdown ( '' ) ;
113- return ;
124+ setLiveCountdown ( '' )
125+ return
114126 }
115127
116128 // 立即更新一次
117- setLiveCountdown ( formatRemaining ( cooldown ) ) ;
129+ setLiveCountdown ( formatRemaining ( cooldown ) )
118130
119131 // 每秒更新
120132 const interval = setInterval ( ( ) => {
121- setLiveCountdown ( formatRemaining ( cooldown ) ) ;
122- } , 1000 ) ;
133+ setLiveCountdown ( formatRemaining ( cooldown ) )
134+ } , 1000 )
123135
124- return ( ) => clearInterval ( interval ) ;
125- } , [ cooldown , formatRemaining ] ) ;
136+ return ( ) => clearInterval ( interval )
137+ } , [ cooldown , formatRemaining ] )
126138
127- if ( ! open || ! cooldown ) return null ;
139+ if ( ! open || ! cooldown ) return null
128140
129- const reasonInfo = REASON_INFO [ cooldown . reason ] || REASON_INFO . unknown ;
130- const Icon = reasonInfo . icon ;
141+ const reasonInfo = REASON_INFO [ cooldown . reason ] || REASON_INFO . unknown
142+ const Icon = reasonInfo . icon
131143
132144 const formatUntilTime = ( until : string ) => {
133- const date = new Date ( until ) ;
145+ const date = new Date ( until )
134146 return date . toLocaleString ( 'zh-CN' , {
135147 month : '2-digit' ,
136148 day : '2-digit' ,
137149 hour : '2-digit' ,
138150 minute : '2-digit' ,
139151 second : '2-digit' ,
140152 hour12 : false ,
141- } ) ;
142- } ;
153+ } )
154+ }
143155
144- const untilDateStr = formatUntilTime ( cooldown . untilTime ) ;
145- const [ datePart , timePart ] = untilDateStr . split ( ' ' ) ;
156+ const untilDateStr = formatUntilTime ( cooldown . untilTime )
157+ const [ datePart , timePart ] = untilDateStr . split ( ' ' )
146158
147159 return createPortal (
148160 < >
@@ -163,7 +175,7 @@ export function CooldownDetailsDialog({
163175 padding : 0 ,
164176 background : 'var(--color-surface-primary)' ,
165177 } }
166- onClick = { ( e ) => e . stopPropagation ( ) }
178+ onClick = { e => e . stopPropagation ( ) }
167179 >
168180 { /* Header with Gradient */ }
169181 < div className = "relative bg-gradient-to-b from-cyan-900/20 to-transparent p-6 pb-4" >
@@ -176,10 +188,15 @@ export function CooldownDetailsDialog({
176188
177189 < div className = "flex flex-col items-center text-center space-y-3" >
178190 < div className = "p-3 rounded-2xl bg-cyan-500/10 border border-cyan-400/20 shadow-[0_0_15px_-3px_rgba(6,182,212,0.2)]" >
179- < Snowflake size = { 28 } className = "text-cyan-400 animate-spin-slow" />
191+ < Snowflake
192+ size = { 28 }
193+ className = "text-cyan-400 animate-spin-slow"
194+ />
180195 </ div >
181196 < div >
182- < h2 className = "text-xl font-bold text-text-primary" > 冷却保护中</ h2 >
197+ < h2 className = "text-xl font-bold text-text-primary" >
198+ 冷却保护中
199+ </ h2 >
183200 < p className = "text-xs text-cyan-500/80 font-medium uppercase tracking-wider mt-1" >
184201 Frozen Protocol Active
185202 </ p >
@@ -189,28 +206,29 @@ export function CooldownDetailsDialog({
189206
190207 { /* Body Content */ }
191208 < div className = "px-6 pb-6 space-y-5" >
192-
193209 { /* Provider Card */ }
194210 < div className = "flex items-center gap-4 p-3 rounded-xl bg-surface-secondary border border-border" >
195211 < div className = "flex-1 min-w-0" >
196- < div className = "flex items-center gap-2 mb-1" >
197- < span className = "text-[10px] font-bold text-text-muted uppercase tracking-wider" > Target Provider</ span >
198- { cooldown . clientType && (
199- < span className = "px-1.5 py-0.5 rounded text-[10px] font-mono bg-surface-hover text-text-secondary" >
200- { cooldown . clientType }
201- </ span >
202- ) }
203- </ div >
204- < div className = "font-semibold text-text-primary truncate" >
205- Provider #{ cooldown . providerID }
206- </ div >
212+ < div className = "flex items-center gap-2 mb-1" >
213+ < span className = "text-[10px] font-bold text-text-muted uppercase tracking-wider" >
214+ Target Provider
215+ </ span >
216+ { cooldown . clientType && (
217+ < span className = "px-1.5 py-0.5 rounded text-[10px] font-mono bg-surface-hover text-text-secondary" >
218+ { cooldown . clientType }
219+ </ span >
220+ ) }
221+ </ div >
222+ < div className = "font-semibold text-text-primary truncate" >
223+ Provider #{ cooldown . providerID }
224+ </ div >
207225 </ div >
208226 </ div >
209227
210228 { /* Reason Section */ }
211229 < div className = { `rounded-xl border p-4 ${ reasonInfo . bgColor } ` } >
212230 < div className = "flex gap-4" >
213- < div className = { `mt-0.5 flex- shrink-0 ${ reasonInfo . color } ` } >
231+ < div className = { `mt-0.5 shrink-0 ${ reasonInfo . color } ` } >
214232 < Icon size = { 20 } />
215233 </ div >
216234 < div >
@@ -226,35 +244,37 @@ export function CooldownDetailsDialog({
226244
227245 { /* Timer Section */ }
228246 < div className = "grid grid-cols-2 gap-3" >
229- { /* Countdown */ }
247+ { /* Countdown */ }
230248 < div className = "col-span-2 relative overflow-hidden rounded-xl bg-gradient-to-br from-cyan-950/30 to-transparent border border-cyan-500/20 p-5 flex flex-col items-center justify-center group" >
231- < div className = "absolute inset-0 bg-cyan-400/5 opacity-50 group-hover:opacity-100 transition-opacity" />
232- < div className = "relative flex items-center gap-1.5 text-cyan-500 mb-1" >
233- < Thermometer size = { 14 } />
234- < span className = "text-[10px] font-bold uppercase tracking-widest" > Remaining</ span >
235- </ div >
236- < div className = "relative font-mono text-4xl font-bold text-cyan-400 tracking-widest tabular-nums drop-shadow-[0_0_8px_rgba(34,211,238,0.3)]" >
237- { liveCountdown }
238- </ div >
249+ < div className = "absolute inset-0 bg-cyan-400/5 opacity-50 group-hover:opacity-100 transition-opacity" />
250+ < div className = "relative flex items-center gap-1.5 text-cyan-500 mb-1" >
251+ < Thermometer size = { 14 } />
252+ < span className = "text-[10px] font-bold uppercase tracking-widest" >
253+ Remaining
254+ </ span >
255+ </ div >
256+ < div className = "relative font-mono text-4xl font-bold text-cyan-400 tracking-widest tabular-nums drop-shadow-[0_0_8px_rgba(34,211,238,0.3)]" >
257+ { liveCountdown }
258+ </ div >
239259 </ div >
240260
241261 { /* Time Details */ }
242262 < div className = "p-3 rounded-xl bg-surface-secondary border border-border flex flex-col items-center justify-center gap-1" >
243- < span className = "text-[10px] text-text-muted uppercase tracking-wider font-bold flex items-center gap-1.5" >
244- < Clock size = { 10 } /> Resume
245- </ span >
246- < div className = "font-mono text-sm font-semibold text-text-primary" >
247- { timePart }
248- </ div >
263+ < span className = "text-[10px] text-text-muted uppercase tracking-wider font-bold flex items-center gap-1.5" >
264+ < Clock size = { 10 } /> Resume
265+ </ span >
266+ < div className = "font-mono text-sm font-semibold text-text-primary" >
267+ { timePart }
268+ </ div >
249269 </ div >
250270
251271 < div className = "p-3 rounded-xl bg-surface-secondary border border-border flex flex-col items-center justify-center gap-1" >
252- < span className = "text-[10px] text-text-muted uppercase tracking-wider font-bold flex items-center gap-1.5" >
253- < Calendar size = { 10 } /> Date
254- </ span >
255- < div className = "font-mono text-sm font-semibold text-text-primary" >
256- { datePart }
257- </ div >
272+ < span className = "text-[10px] text-text-muted uppercase tracking-wider font-bold flex items-center gap-1.5" >
273+ < Calendar size = { 10 } /> Date
274+ </ span >
275+ < div className = "font-mono text-sm font-semibold text-text-primary" >
276+ { datePart }
277+ </ div >
258278 </ div >
259279 </ div >
260280
@@ -268,14 +288,21 @@ export function CooldownDetailsDialog({
268288 < span className = "absolute inset-0 bg-gradient-to-r from-cyan-500 to-blue-600 rounded-xl" />
269289 < div className = "relative flex items-center justify-center gap-2 rounded-[11px] bg-surface-primary group-hover:bg-transparent px-4 py-3 transition-colors" >
270290 { isClearing ? (
271- < >
272- < div className = "h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
273- < span className = "text-sm font-bold text-white" > Thawing...</ span >
274- </ >
291+ < >
292+ < div className = "h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
293+ < span className = "text-sm font-bold text-white" >
294+ Thawing...
295+ </ span >
296+ </ >
275297 ) : (
276298 < >
277- < Zap size = { 16 } className = "text-cyan-400 group-hover:text-white transition-colors" />
278- < span className = "text-sm font-bold text-cyan-400 group-hover:text-white transition-colors" > 立即解冻 (Force Thaw)</ span >
299+ < Zap
300+ size = { 16 }
301+ className = "text-cyan-400 group-hover:text-white transition-colors"
302+ />
303+ < span className = "text-sm font-bold text-cyan-400 group-hover:text-white transition-colors" >
304+ 立即解冻 (Force Thaw)
305+ </ span >
279306 </ >
280307 ) }
281308 </ div >
@@ -287,13 +314,13 @@ export function CooldownDetailsDialog({
287314 className = "w-full flex items-center justify-center gap-2 rounded-xl border border-border bg-surface-secondary hover:bg-surface-hover px-4 py-3 text-sm font-medium text-text-secondary transition-colors disabled:opacity-50"
288315 >
289316 { isDisabling ? (
290- < div className = "h-3 w-3 animate-spin rounded-full border-2 border-current/30 border-t-current" />
317+ < div className = "h-3 w-3 animate-spin rounded-full border-2 border-current/30 border-t-current" />
291318 ) : (
292319 < Ban size = { 16 } />
293320 ) }
294321 { isDisabling ? 'Disabling...' : '禁用此路由 (Disable Route)' }
295322 </ button >
296-
323+
297324 < div className = "flex items-start gap-2 rounded-lg bg-surface-secondary/50 p-2.5 text-[11px] text-text-muted" >
298325 < Activity size = { 12 } className = "mt-0.5 shrink-0" />
299326 < p > 强制解冻可能导致请求因根本原因未解决而再次失败。</ p >
@@ -303,5 +330,5 @@ export function CooldownDetailsDialog({
303330 </ div >
304331 </ > ,
305332 document . body
306- ) ;
333+ )
307334}
0 commit comments