@@ -130,12 +130,53 @@ function IconEntryEmpty() {
130130 return < svg viewBox = "0 0 24 24" width = "28" height = "28" fill = "currentColor" > < path d = "M10 3h4v4h5a2 2 0 0 1 2 2v7h-2V9h-4v4H9V9H5v10h7v2H5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V3zm1 1v3h2V4h-2zm7 14 3 3-1.4 1.4L17 19.8l-2.6 2.6L13 21l3-3-3-3 1.4-1.4L17 16.2l2.6-2.6L21 15l-3 3z" /> </ svg >
131131}
132132
133- function formatCertificateLabel ( certificate : { nice_name ?: string | null ; domain_names ?: string [ ] ; provider ?: string | null } | null | undefined ) {
133+ function getOrdinal ( day : number ) : string {
134+ if ( day >= 11 && day <= 13 ) return 'th'
135+ const last = day % 10
136+ if ( last === 1 ) return 'st'
137+ if ( last === 2 ) return 'nd'
138+ if ( last === 3 ) return 'rd'
139+ return 'th'
140+ }
141+
142+ function formatExpiryDate ( isoString : string , locale : string ) : string {
143+ const date = new Date ( isoString )
144+ if ( isNaN ( date . getTime ( ) ) ) return ''
145+ const isZh = String ( locale || '' ) . toLowerCase ( ) . startsWith ( 'zh' )
146+ if ( isZh ) {
147+ const y = date . getFullYear ( )
148+ const m = date . getMonth ( ) + 1
149+ const d = date . getDate ( )
150+ const hh = String ( date . getHours ( ) ) . padStart ( 2 , '0' )
151+ const mm = String ( date . getMinutes ( ) ) . padStart ( 2 , '0' )
152+ return `${ y } 年${ m } 月${ d } 日 ${ hh } :${ mm } `
153+ }
154+ const months = [ 'January' , 'February' , 'March' , 'April' , 'May' , 'June' , 'July' , 'August' , 'September' , 'October' , 'November' , 'December' ]
155+ const day = date . getDate ( )
156+ const month = months [ date . getMonth ( ) ] ?? ''
157+ const year = date . getFullYear ( )
158+ let hours = date . getHours ( )
159+ const ampm = hours >= 12 ? 'PM' : 'AM'
160+ hours = hours % 12 || 12
161+ const minutes = String ( date . getMinutes ( ) ) . padStart ( 2 , '0' )
162+ return `${ day } ${ getOrdinal ( day ) } ${ month } ${ year } , ${ hours } :${ minutes } ${ ampm } `
163+ }
164+
165+ function formatCertificateLabel (
166+ certificate : { nice_name ?: string | null ; domain_names ?: string [ ] ; provider ?: string | null ; expires_on ?: string | null } | null | undefined ,
167+ locale ?: string ,
168+ ) {
134169 if ( ! certificate ) return ''
135- if ( certificate . nice_name ?. trim ( ) ) return certificate . nice_name . trim ( )
136- if ( certificate . domain_names ?. length ) return certificate . domain_names . join ( ', ' )
137- if ( certificate . provider ?. trim ( ) ) return certificate . provider . trim ( )
138- return ''
170+ const name = certificate . nice_name ?. trim ( ) || certificate . domain_names ?. join ( ', ' ) || certificate . provider ?. trim ( ) || ''
171+ if ( ! name ) return ''
172+ if ( certificate . expires_on ) {
173+ const expiryLabel = formatExpiryDate ( certificate . expires_on , locale || '' )
174+ if ( expiryLabel ) {
175+ const isZh = String ( locale || '' ) . toLowerCase ( ) . startsWith ( 'zh' )
176+ return isZh ? `${ name } (过期:${ expiryLabel } )` : `${ name } (Expires: ${ expiryLabel } )`
177+ }
178+ }
179+ return name
139180}
140181
141182function formatAccountLabel ( key : string , t : ( key : string ) => string ) {
@@ -189,7 +230,7 @@ function getConnectionTitle(t: (key: string, options?: Record<string, unknown>)
189230}
190231
191232export function MyAppAccessPanel ( { appId, env, isComposeApp, onUpdated, scopeRect, isDarkMode = false } : MyAppAccessPanelProps ) {
192- const { t } = useTranslation ( 'shell' )
233+ const { t, i18n } = useTranslation ( 'shell' )
193234 const palette = getSurfacePalette ( isDarkMode )
194235 const [ selectedProxyId , setSelectedProxyId ] = useState < number | null > ( null )
195236 const [ selectedDomainName , setSelectedDomainName ] = useState < string | null > ( null )
@@ -269,7 +310,7 @@ export function MyAppAccessPanel({ appId, env, isComposeApp, onUpdated, scopeRec
269310 ( ) => data ?. certificates . find ( ( certificate ) => certificate . id === currentProxyHost ?. certificate_id ) ?? null ,
270311 [ currentProxyHost ?. certificate_id , data ?. certificates ] ,
271312 )
272- const currentCertificateLabel = currentProxyHost ?. certificate_name || formatCertificateLabel ( currentCertificate )
313+ const currentCertificateLabel = currentProxyHost ?. certificate_name || formatCertificateLabel ( currentCertificate , i18n . resolvedLanguage )
273314 const containerOptions = useMemo ( ( ) => {
274315 const seen = new Set < string > ( )
275316 return ( data ?. candidates ?? [ ] ) . filter ( ( candidate ) => {
@@ -1465,7 +1506,7 @@ ${customCertIntermediate.trim()}`
14651506 < option value = "none" > { t ( 'myAppsDetailPage.accessPanel.selectCertPlaceholder' ) } </ option >
14661507 { availableCertificates . map ( ( cert ) => (
14671508 < option key = { cert . id } value = { cert . id } >
1468- { formatCertificateLabel ( cert ) || `#${ cert . id } ` }
1509+ { formatCertificateLabel ( cert , i18n . resolvedLanguage ) || `#${ cert . id } ` }
14691510 </ option >
14701511 ) ) }
14711512 </ select >
@@ -1676,7 +1717,15 @@ ${customCertIntermediate.trim()}`
16761717 scopeRect = { scopeRect ?? null }
16771718 contentStrategy = "viewport-fixed"
16781719 darkMode = { isDarkMode }
1679- sx = { { zIndex : 1510 } }
1720+ sx = { {
1721+ zIndex : 1510 ,
1722+ '& .MuiDialog-container' : {
1723+ alignItems : 'flex-start' ,
1724+ justifyContent : 'center' ,
1725+ pt : { xs : 3 , md : 3 } ,
1726+ pb : { xs : 1.5 , md : 2.5 } ,
1727+ } ,
1728+ } }
16801729 paperSx = { {
16811730 maxWidth : '480px' ,
16821731 borderRadius : 0 ,
0 commit comments