@@ -975,12 +975,14 @@ function renderServerCard(s) {
975975 const alloc = s . allocationDetails ;
976976 const isInstalling = s . status === 'installing' || s . installed === 0 || s . installed === '0' || s . installed === false ;
977977 const isSuspended = s . status === 'suspended' ;
978- const statusClass = isSuspended ? 'status-suspended' : ( isInstalling ? 'status-installing' : 'status-active' ) ;
979- const statusLabel = isSuspended ? 'Suspended' : ( isInstalling ? 'Installing' : 'Active' ) ;
980- const allocStr = alloc ? `${ alloc . alias || alloc . nodeFqdn || alloc . ip } :${ alloc . port } ` : ( s . nodeFqdn || `Node #${ s . node } ` ) ;
981978 const meta = s . serverMeta ;
982979 const days = meta ? daysRemaining ( meta . expires_at ) : null ;
983980 const canRenew = days !== null && days <= 7 && days >= - 7 ;
981+ const isAdminSuspended = isSuspended && meta ?. suspended_by === 'admin' ;
982+ const isExpiredRenewable = isSuspended && canRenew && ! isAdminSuspended ;
983+ const statusClass = isAdminSuspended ? 'status-suspended' : ( isExpiredRenewable ? 'status-expired' : ( isInstalling ? 'status-installing' : 'status-active' ) ) ;
984+ const statusLabel = isAdminSuspended ? 'Suspended' : ( isExpiredRenewable ? 'Expired' : ( isInstalling ? 'Installing' : 'Active' ) ) ;
985+ const allocStr = alloc ? `${ alloc . alias || alloc . nodeFqdn || alloc . ip } :${ alloc . port } ` : ( s . nodeFqdn || `Node #${ s . node } ` ) ;
984986 const expClass = days !== null && days <= 0 ? 'expired' : ( days !== null && days <= 7 ? 'expiring' : '' ) ;
985987 return html `
986988 < div class ="server-card ">
@@ -1017,13 +1019,15 @@ function renderServerRow(s) {
10171019 const alloc = s . allocationDetails ;
10181020 const isInstalling = s . status === 'installing' || s . installed === 0 || s . installed === '0' || s . installed === false ;
10191021 const isSuspended = s . status === 'suspended' ;
1020- const powerState = s . currentState ? s . currentState . charAt ( 0 ) . toUpperCase ( ) + s . currentState . slice ( 1 ) : null ;
1021- const statusClass = isSuspended ? 'status-suspended' : ( isInstalling ? 'status-installing' : ( powerState === 'Offline' ? 'status-offline' : 'status-active' ) ) ;
1022- const statusLabel = isSuspended ? 'Suspended' : ( isInstalling ? 'Installing' : ( powerState || 'Active' ) ) ;
1023- const allocStr = alloc ? `${ alloc . alias || alloc . nodeFqdn || alloc . ip } :${ alloc . port } ` : ( s . nodeFqdn || `Node #${ s . node } ` ) ;
10241022 const meta = s . serverMeta ;
10251023 const days = meta ? daysRemaining ( meta . expires_at ) : null ;
10261024 const canRenew = days !== null && days <= 7 && days >= - 7 ;
1025+ const isAdminSuspended = isSuspended && meta ?. suspended_by === 'admin' ;
1026+ const isExpiredRenewable = isSuspended && canRenew && ! isAdminSuspended ;
1027+ const powerState = s . currentState ? s . currentState . charAt ( 0 ) . toUpperCase ( ) + s . currentState . slice ( 1 ) : null ;
1028+ const statusClass = isAdminSuspended ? 'status-suspended' : ( isExpiredRenewable ? 'status-expired' : ( isInstalling ? 'status-installing' : ( powerState === 'Offline' ? 'status-offline' : 'status-active' ) ) ) ;
1029+ const statusLabel = isAdminSuspended ? 'Suspended' : ( isExpiredRenewable ? 'Expired' : ( isInstalling ? 'Installing' : ( powerState || 'Active' ) ) ) ;
1030+ const allocStr = alloc ? `${ alloc . alias || alloc . nodeFqdn || alloc . ip } :${ alloc . port } ` : ( s . nodeFqdn || `Node #${ s . node } ` ) ;
10271031 return html `
10281032 < tr >
10291033 < td > < strong > < a href ="/server/ ${ s . id } " onclick ="event.preventDefault();navigateTo('server/${ s . id } ') " style ="color:inherit;text-decoration:none "> ${ s . name } </ a > </ strong > </ td >
@@ -1923,10 +1927,12 @@ async function renderServerDetail(serverId) {
19231927 const allocStr = alloc ? `${ alloc . alias || alloc . nodeFqdn || alloc . ip } :${ alloc . port } ` : ( s . nodeFqdn || `Node #${ s . node } ` ) ;
19241928 const isInstalling = s . status === 'installing' || s . installed === 0 || s . installed === '0' || s . installed === false ;
19251929 const isSuspended = s . status === 'suspended' ;
1926- const statusClass = isSuspended ? 'status-suspended' : ( isInstalling ? 'status-installing' : 'status-active' ) ;
1927- const statusLabel = isSuspended ? 'Suspended' : ( isInstalling ? 'Installing' : 'Active' ) ;
19281930 const days = meta ? daysRemaining ( meta . expires_at ) : null ;
19291931 const canRenew = days !== null && days <= 7 && days >= - 7 ;
1932+ const isAdminSuspended = isSuspended && meta ?. suspended_by === 'admin' ;
1933+ const isExpiredRenewable = isSuspended && canRenew && ! isAdminSuspended ;
1934+ const statusClass = isAdminSuspended ? 'status-suspended' : ( isExpiredRenewable ? 'status-expired' : ( isInstalling ? 'status-installing' : 'status-active' ) ) ;
1935+ const statusLabel = isAdminSuspended ? 'Suspended' : ( isExpiredRenewable ? 'Expired' : ( isInstalling ? 'Installing' : 'Active' ) ) ;
19301936 const expClass = days !== null && days <= 0 ? 'expired' : ( days !== null && days <= 7 ? 'expiring' : '' ) ;
19311937
19321938 const activeTab = state . serverDetailTab || 'info' ;
@@ -1946,12 +1952,18 @@ async function renderServerDetail(serverId) {
19461952 </ div >
19471953 </ div >
19481954
1949- ${ isSuspended && meta ?. suspend_reason ? html `
1955+ ${ isAdminSuspended && meta ?. suspend_reason ? html `
19501956 < div style ="background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);border-radius:8px;padding:16px;margin-bottom:24px ">
19511957 < div style ="font-weight:700;color:var(--accent-red);margin-bottom:4px "> Server Suspended</ div >
19521958 < div style ="color:var(--text-secondary);font-size:0.88rem "> ${ meta . suspend_reason } </ div >
19531959 </ div >
19541960 ` : '' }
1961+ ${ isExpiredRenewable ? html `
1962+ < div style ="background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);border-radius:8px;padding:16px;margin-bottom:24px ">
1963+ < div style ="font-weight:700;color:var(--accent-orange);margin-bottom:4px "> Server Expired</ div >
1964+ < div style ="color:var(--text-secondary);font-size:0.88rem "> This server has expired. Renew it to reactivate it instantly.</ div >
1965+ </ div >
1966+ ` : '' }
19551967
19561968 < div class ="tabs " id ="server-detail-tabs ">
19571969 < button class ="tab ${ activeTab === 'info' ? 'active' : '' } " data-tab ="info "> Info</ button >
@@ -2003,8 +2015,8 @@ async function renderServerDetail(serverId) {
20032015 < div class ="detail-list ">
20042016 < div class ="detail-item "> < span class ="detail-label "> Created</ span > < span class ="detail-value "> ${ formatDate ( meta . created_at ) } </ span > </ div >
20052017 < div class ="detail-item ${ expClass } "> < span class ="detail-label "> Expires</ span > < span class ="detail-value "> ${ formatDate ( meta . expires_at ) } ${ days !== null ? '(' + ( days > 0 ? days + ' days' : 'Expired' ) + ')' : '' } </ span > </ div >
2006- < div class ="detail-item "> < span class ="detail-label "> Status</ span > < span class ="detail-value " style ="text-transform:capitalize "> ${ meta . status } </ span > </ div >
2007- ${ meta . status === 'suspended' && meta . suspend_reason ? html `
2018+ < div class ="detail-item "> < span class ="detail-label "> Status</ span > < span class ="detail-value " style ="text-transform:capitalize "> ${ isExpiredRenewable ? 'expired' : meta . status } </ span > </ div >
2019+ ${ isAdminSuspended && meta . suspend_reason ? html `
20082020 < div class ="detail-item "> < span class ="detail-label "> Reason</ span > < span class ="detail-value " style ="color:var(--accent-red) "> ${ meta . suspend_reason } </ span > </ div >
20092021 ` : '' }
20102022 </ div >
@@ -2037,13 +2049,27 @@ async function renderServerDetail(serverId) {
20372049 </ div >
20382050
20392051 < div id ="server-tab-actions " class ="tab-content " style ="display: ${ activeTab === 'actions' ? 'block' : 'none' } ">
2040- ${ isSuspended ? html `
2052+ ${ isAdminSuspended ? html `
20412053 < div style ="text-align:center;padding:48px 24px ">
20422054 < svg width ="64 " height ="64 " viewBox ="0 0 24 24 " fill ="none " stroke ="var(--accent-red) " stroke-width ="1.5 " style ="margin-bottom:16px "> < rect x ="3 " y ="11 " width ="18 " height ="11 " rx ="2 " ry ="2 "/> < path d ="M7 11V7a5 5 0 0110 0v4 "/> </ svg >
20432055 < h2 style ="margin:0 0 8px 0;color:var(--text-primary) "> Server Suspended</ h2 >
2044- < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 0 4px 0 "> This server has been suspended. No actions are available.</ p >
2056+ < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 0 4px 0 "> This server has been suspended by an administrator . No actions are available.</ p >
20452057 < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 "> Please contact support via < a href ="https://discord.zero-host.org " target ="_blank " style ="color:var(--accent-1);text-decoration:underline "> Discord</ a > for assistance.</ p >
20462058 </ div >
2059+ ` : isExpiredRenewable ? html `
2060+ < div style ="text-align:center;padding:48px 24px ">
2061+ < svg width ="64 " height ="64 " viewBox ="0 0 24 24 " fill ="none " stroke ="var(--accent-orange) " stroke-width ="1.5 " style ="margin-bottom:16px "> < circle cx ="12 " cy ="12 " r ="10 "/> < path d ="M12 6v6l4 2 "/> </ svg >
2062+ < h2 style ="margin:0 0 8px 0;color:var(--text-primary) "> Server Expired</ h2 >
2063+ < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 0 4px 0 "> This server has expired. Renew it to reactivate it instantly and get 90 more days.</ p >
2064+ < button class ="btn btn-primary btn-renew-server " data-server-id ="${ s . id } " style ="margin-top:16px "> Renew Server (90 days)</ button >
2065+ </ div >
2066+ ` : isSuspended ? html `
2067+ < div style ="text-align:center;padding:48px 24px ">
2068+ < svg width ="64 " height ="64 " viewBox ="0 0 24 24 " fill ="none " stroke ="var(--accent-red) " stroke-width ="1.5 " style ="margin-bottom:16px "> < rect x ="3 " y ="11 " width ="18 " height ="11 " rx ="2 " ry ="2 "/> < path d ="M7 11V7a5 5 0 0110 0v4 "/> </ svg >
2069+ < h2 style ="margin:0 0 8px 0;color:var(--text-primary) "> Server Expired</ h2 >
2070+ < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 0 4px 0 "> This server has been expired for too long. Please contact support to renew.</ p >
2071+ < p style ="color:var(--text-secondary);font-size:0.95rem;margin:0 "> Reach out via < a href ="https://discord.zero-host.org " target ="_blank " style ="color:var(--accent-1);text-decoration:underline "> Discord</ a > for assistance.</ p >
2072+ </ div >
20472073 ` : html `
20482074 < div class ="action-card ">
20492075 < div class ="action-card-header ">
0 commit comments