@@ -4,14 +4,18 @@ import { useDebug } from '../../context/DebugContext';
44import { useI18n } from '../../context/I18nContext' ;
55import type { AuthFile , AuthModel } from '../../types' ;
66import { toErrorMessage } from '../../utils/error' ;
7+ import type { AccountUsageSummary } from '../../features/accounts/model/accountUsage' ;
8+ import AccountHealthBar from '../../features/accounts/components/AccountHealthBar' ;
79import { canCopyRawContent , copyRawContent , RAW_CONTENT_COPY_RESET_MS } from './accountDetailClipboard' ;
810
911interface AccountDetailModalProps {
1012 account : AuthFile ;
13+ usageSummary ?: AccountUsageSummary ;
1114 canStartReauth ?: boolean ;
1215 isReauthing ?: boolean ;
1316 onClose : ( ) => void ;
1417 onStartReauth ?: ( ) => void ;
18+ onCancelReauth ?: ( ) => void ;
1519}
1620
1721type DetailField = readonly [ string , string ] ;
@@ -39,10 +43,12 @@ function getModelLabel(model: AuthModel): string {
3943
4044export default function AccountDetailModal ( {
4145 account,
46+ usageSummary,
4247 canStartReauth = false ,
4348 isReauthing = false ,
4449 onClose,
4550 onStartReauth,
51+ onCancelReauth,
4652} : AccountDetailModalProps ) {
4753 const { t } = useI18n ( ) ;
4854 const { trackRequest } = useDebug ( ) ;
@@ -55,8 +61,6 @@ export default function AccountDetailModal({
5561 const [ sanitizeState , setSanitizeState ] = useState < 'idle' | 'success' | 'error' > ( 'idle' ) ;
5662 const [ viewMode , setViewMode ] = useState < 'raw' | 'sanitized' > ( 'raw' ) ;
5763 const [ sanitizing , setSanitizing ] = useState ( false ) ;
58- const [ verifyResult , setVerifyResult ] = useState ( '' ) ;
59- const [ verifying , setVerifying ] = useState ( false ) ;
6064
6165 const detailFields = useMemo < DetailField [ ] > (
6266 ( ) => [
@@ -65,11 +69,29 @@ export default function AccountDetailModal({
6569 [ t ( 'accounts.size' ) , account . size ? `${ account . size } B` : '—' ] ,
6670 [ t ( 'common.status' ) , account . status || '—' ] ,
6771 [ t ( 'common.enable' ) , account . disabled ? 'NO' : 'YES' ] ,
68- [ 'REFRESH' , formatRefreshValue ( account . lastRefresh ) ] ,
72+ [ t ( 'accounts.last_refresh' ) , formatRefreshValue ( account . lastRefresh ) ] ,
6973 ] ,
7074 [ account , t ]
7175 ) ;
7276
77+ const statisticsFields = useMemo < DetailField [ ] > (
78+ ( ) => [
79+ [
80+ t ( 'accounts.success_rate' ) ,
81+ usageSummary ?. successRate !== null && usageSummary ?. successRate !== undefined
82+ ? `${ Math . round ( usageSummary . successRate ) } %`
83+ : t ( 'accounts.no_recent_activity' ) ,
84+ ] ,
85+ [ t ( 'accounts.recent_success' ) , String ( usageSummary ?. success ?? 0 ) ] ,
86+ [ t ( 'accounts.recent_failure' ) , String ( usageSummary ?. failure ?? 0 ) ] ,
87+ [
88+ t ( 'accounts.average_latency' ) ,
89+ usageSummary ?. averageLatencyMs ? `${ usageSummary . averageLatencyMs } ms` : '—' ,
90+ ] ,
91+ ] ,
92+ [ t , usageSummary ]
93+ ) ;
94+
7395 useEffect ( ( ) => {
7496 let mounted = true ;
7597
@@ -193,30 +215,14 @@ export default function AccountDetailModal({
193215 }
194216 }
195217
196- async function verify ( ) {
197- setVerifying ( true ) ;
198- setVerifyResult ( 'VERIFYING...' ) ;
199- try {
200- await trackRequest ( 'GetAuthFileModels' , { name : account . name , mode : 'verify' } , ( ) =>
201- GetAuthFileModels ( account . name )
202- ) ;
203- setVerifyResult ( '✓ VALID' ) ;
204- } catch ( error ) {
205- console . error ( error ) ;
206- setVerifyResult ( '✗ FAILED' ) ;
207- } finally {
208- setVerifying ( false ) ;
209- }
210- }
211-
212218 return (
213219 < div
214220 className = "fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-8 backdrop-blur-sm"
215221 data-collaboration-id = "MODAL_ACCOUNT_DETAIL"
216222 onClick = { onClose }
217223 >
218224 < div
219- className = "flex max-h-[90vh] w-full max-w-2xl flex-col border-2 border-[var(--border-color)] bg-[var(--bg-main)] shadow-hard shadow-[var(--shadow-color)]"
225+ className = "flex max-h-[90vh] w-full max-w-2xl flex-col border-2 border-[var(--border-color)] bg-[var(--bg-main)] shadow-hard shadow-[var(--shadow-color)]"
220226 onClick = { ( event : ClickEventLike ) => event . stopPropagation ( ) }
221227 >
222228 < header className = "flex items-center justify-between border-b-2 border-[var(--border-color)] bg-[var(--bg-main)] px-6 py-4" >
@@ -231,11 +237,10 @@ export default function AccountDetailModal({
231237 < div className = "flex items-center gap-3" >
232238 { canStartReauth ? (
233239 < button
234- onClick = { onStartReauth }
235- disabled = { isReauthing }
240+ onClick = { isReauthing ? onCancelReauth : onStartReauth }
236241 className = "btn-swiss !px-3 !py-1 !text-[9px]"
237242 >
238- { isReauthing ? t ( 'accounts.reauth_pending ' ) : t ( 'accounts.reauth' ) }
243+ { isReauthing ? t ( 'common.cancel ' ) : t ( 'accounts.reauth' ) }
239244 </ button >
240245 ) : null }
241246 < button onClick = { onClose } className = "btn-swiss !p-1 !shadow-none hover:bg-[var(--bg-surface)]" >
@@ -256,29 +261,30 @@ export default function AccountDetailModal({
256261 ) ) }
257262 </ div >
258263
259- { canStartReauth ? (
260- < section className = "space-y-4 border-b-2 border-dashed border-[var(--border-color)] pb-8" >
261- < div className = "flex items-center gap-2 text-[9px] font-black uppercase tracking-widest text-[var(--text-muted)]" >
262- < span className = "h-2 w-2 bg-[var(--border-color)]" > </ span >
263- ACCOUNT_ACTIONS
264+ < section className = "space-y-4 border-b-2 border-dashed border-[var(--border-color)] pb-8" >
265+ < div className = "flex items-center justify-between gap-4" >
266+ < div className = "text-[9px] font-black uppercase tracking-[0.2em] text-[var(--text-muted)]" >
267+ { t ( 'accounts.recent_health' ) }
264268 </ div >
265- < div className = "flex items-center justify-between gap-4 border-2 border-[var(--border-color)] bg-[var(--bg-surface)] p-4" >
266- < div className = "space-y-1" >
267- < div className = "text-[11px] font-black uppercase text-[var(--text-primary)]" > { t ( 'accounts.reauth' ) } </ div >
268- < div className = "text-[10px] font-bold uppercase tracking-wide text-[var(--text-muted)]" >
269- { t ( 'accounts.reauth_detail_hint' ) }
270- </ div >
271- </ div >
272- < button
273- onClick = { onStartReauth }
274- disabled = { isReauthing }
275- className = "btn-swiss shrink-0 !px-3 !py-2 !text-[9px]"
276- >
277- { isReauthing ? t ( 'accounts.reauth_pending' ) : t ( 'accounts.reauth' ) }
278- </ button >
269+ < div className = "text-[9px] font-black uppercase tracking-[0.16em] text-[var(--text-primary)]" >
270+ { usageSummary ?. hasData ? t ( 'accounts.stability_signal_synced' ) : t ( 'accounts.no_recent_activity' ) }
279271 </ div >
280- </ section >
281- ) : null }
272+ </ div >
273+
274+ { usageSummary ?. hasData ? < AccountHealthBar summary = { usageSummary } /> : null }
275+
276+ < div className = "grid gap-3 md:grid-cols-2 xl:grid-cols-4" >
277+ { statisticsFields . map ( ( [ label , value ] ) => (
278+ < div
279+ key = { label }
280+ className = "space-y-1 border-2 border-dashed border-[var(--border-color)] bg-[var(--bg-surface)] px-3 py-3"
281+ >
282+ < div className = "text-[8px] font-black uppercase tracking-[0.2em] text-[var(--text-muted)]" > { label } </ div >
283+ < div className = "text-[12px] font-black uppercase tracking-[0.06em] text-[var(--text-primary)]" > { value } </ div >
284+ </ div >
285+ ) ) }
286+ </ div >
287+ </ section >
282288
283289 < section className = "space-y-4" >
284290 < div className = "flex items-center justify-between" >
@@ -310,13 +316,13 @@ export default function AccountDetailModal({
310316 < span className = "h-2 w-2 bg-[var(--border-color)]" > </ span >
311317 { viewMode === 'sanitized' ? 'SANITIZED_SOURCE_DATA' : 'RAW_SOURCE_DATA' }
312318 </ div >
313- < div className = "flex items-center gap-3" >
314- { copyState !== 'idle' || sanitizeState !== 'idle' ? (
315- < span className = "text-[9px] font-black uppercase tracking-[0.14em] text-[var(--text-muted )]" >
316- { copyState === 'success' || sanitizeState === 'success'
317- ? t ( 'accounts.copy_done' )
318- : t ( 'accounts.copy_failed' ) }
319- </ span >
319+ < div className = "flex items-center gap-3" >
320+ { copyState !== 'idle' || sanitizeState !== 'idle' ? (
321+ < span className = "text-[9px] font-black uppercase tracking-[0.14em] text-[var(--border-color )]" >
322+ { copyState === 'success' || sanitizeState === 'success'
323+ ? t ( 'accounts.copy_done' )
324+ : t ( 'accounts.copy_failed' ) }
325+ </ span >
320326 ) : null }
321327 { loadingRaw ? (
322328 < span className = "animate-pulse text-[9px] font-black text-[var(--text-muted)]" > FETCHING_FS...</ span >
@@ -367,25 +373,7 @@ export default function AccountDetailModal({
367373 </ section >
368374 </ div >
369375
370- < footer className = "flex items-center justify-between border-t-2 border-[var(--border-color)] bg-[var(--bg-surface)] px-6 py-4" >
371- < div className = "flex items-center gap-4" >
372- < button
373- onClick = { verify }
374- disabled = { verifying }
375- className = "btn-swiss bg-[var(--border-color)] !text-[var(--bg-main)]"
376- >
377- { verifying ? 'VERIFYING...' : 'VERIFY_ACCOUNT' }
378- </ button >
379- { verifyResult ? (
380- < span
381- className = { `text-[10px] font-black italic ${
382- verifyResult . includes ( '✓' ) ? 'text-green-600' : 'text-red-600'
383- } `}
384- >
385- { verifyResult }
386- </ span >
387- ) : null }
388- </ div >
376+ < footer className = "flex items-center justify-end border-t-2 border-[var(--border-color)] bg-[var(--bg-surface)] px-6 py-4" >
389377 < button onClick = { onClose } className = "btn-swiss" >
390378 { t ( 'common.close' ) }
391379 </ button >
0 commit comments