@ -2,8 +2,8 @@ import {buildHomeBanners} from 'app/client/components/Banners';
import { makeT } from 'app/client/lib/localization' ;
import { localStorageJsonObs } from 'app/client/lib/localStorageObs' ;
import { getTimeFromNow } from 'app/client/lib/timeUtils' ;
import { AdminChecks , ProbeDetails } from 'app/client/models/AdminChecks' ;
import { AppModel , getHomeUrl , reportError } from 'app/client/models/AppModel' ;
import { AdminChecks } from 'app/client/models/AdminChecks' ;
import { urlState } from 'app/client/models/gristUrlState' ;
import { AppHeader } from 'app/client/ui/AppHeader' ;
import { leftPanelBasic } from 'app/client/ui/LeftPanelCommon' ;
@ -17,7 +17,7 @@ import {toggle} from 'app/client/ui2018/checkbox';
import { mediaSmall , testId , theme , vars } from 'app/client/ui2018/cssVars' ;
import { icon } from 'app/client/ui2018/icons' ;
import { cssLink , makeLinks } from 'app/client/ui2018/links' ;
import { SandboxingBootProbeDetails} from 'app/common/BootProbe' ;
import { BootProbeInfo, BootProbeResult , SandboxingBootProbeDetails} from 'app/common/BootProbe' ;
import { commonUrls , getPageTitleSuffix } from 'app/common/gristUrls' ;
import { InstallAPI , InstallAPIImpl , LatestVersion } from 'app/common/InstallAPI' ;
import { naturalCompare } from 'app/common/SortFunc' ;
@ -39,7 +39,7 @@ export class AdminPanel extends Disposable {
private readonly _installAPI : InstallAPI = new InstallAPIImpl ( getHomeUrl ( ) ) ;
private _checks : AdminChecks ;
constructor ( private _appModel : AppModel ) {
constructor ( private _appModel : AppModel , private _fullScreen : boolean = false ) {
super ( ) ;
document . title = getAdminPanelName ( ) + getPageTitleSuffix ( getGristConfig ( ) ) ;
this . _checks = new AdminChecks ( this ) ;
@ -49,6 +49,9 @@ export class AdminPanel extends Disposable {
this . _checks . fetchAvailableChecks ( ) . catch ( err = > {
reportError ( err ) ;
} ) ;
if ( this . _fullScreen ) {
return dom . create ( this . _buildMainContent . bind ( this ) ) ;
}
const panelOpen = Observable . create ( this , false ) ;
return pagePanels ( {
leftPanel : {
@ -119,6 +122,23 @@ export class AdminPanel extends Disposable {
} ) ,
this . _buildUpdates ( owner ) ,
) ,
cssSection (
cssSectionTitle ( t ( 'Self Checks' ) ) ,
this . _buildProbeItems ( owner , {
showRedundant : false ,
showNovel : true ,
} ) ,
this . _buildItem ( owner , {
id : 'probe-other' ,
name : 'more...' ,
description : '' ,
value : '' ,
expandedContent : this._buildProbeItems ( owner , {
showRedundant : true ,
showNovel : false ,
} ) ,
} ) ,
) ,
testId ( 'admin-panel' ) ,
) ;
}
@ -145,6 +165,7 @@ export class AdminPanel extends Disposable {
}
private _buildSandboxingNotice() {
// TODO: reconcile with AdminChecks text for sandboxing.
return [
t ( ' Grist allows for very powerful formulas , using Python . \
We recommend setting the environment variable GRIST_SANDBOX_FLAVOR to gvisor \
@ -420,12 +441,100 @@ isolated from other documents and isolated from the network.'),
)
} ) ;
}
/ * *
* Show the results of various checks . Of the checks , some are considered
* "redundant" ( already covered elsewhere in the Admin Panel ) and the
* remainder are "novel" .
* /
private _buildProbeItems ( owner : MultiHolder , options : {
showRedundant : boolean ,
showNovel : boolean ,
} ) {
return dom . domComputed (
use = > [
. . . use ( this . _checks . probes ) . map ( probe = > {
const isRedundant = probe . id === 'sandboxing' ;
const show = isRedundant ? options.showRedundant : options.showNovel ;
if ( ! show ) { return null ; }
const req = this . _checks . requestCheck ( probe ) ;
return this . _buildProbeItem ( owner , req . probe , use ( req . result ) , req . details ) ;
} ) ,
]
) ;
}
/ * *
* Show the result of an individual check .
* /
private _buildProbeItem ( owner : MultiHolder ,
info : BootProbeInfo ,
result : BootProbeResult ,
details : ProbeDetails | undefined ) {
const out : ( HTMLElement | string | null ) [ ] = [ ] ;
if ( result . verdict ) {
out . push ( dom ( 'pre' , result . verdict ) ) ;
}
if ( result . success !== undefined ) {
out . push ( dom ( 'p' ,
result . success ? 'Check succeeded.' : 'Check failed.' ) ) ;
}
if ( result . details ) {
for ( const [ key , val ] of Object . entries ( result . details ) ) {
out . push ( dom (
'div' ,
cssLabel ( key ) ,
dom ( 'input' , dom . prop ( 'value' , JSON . stringify ( val ) ) ) ) ) ;
}
}
const status = ( result . success !== undefined ) ?
( result . success ? '✅' : '❗' ) : '―' ;
return this . _buildItem ( owner , {
id : ` probe- ${ info . id } ` ,
name : info.id ,
description : info.name ,
value : cssStatus ( status ) ,
expandedContent : [
result . verdict ? dom ( 'pre' , result . verdict ) : null ,
( result . success === undefined ) ? null :
dom ( 'p' ,
result . success ? 'Check succeeded.' : 'Check failed.' ) ,
( result . done !== true ) ? null :
dom ( 'p' , 'No fault detected.' ) ,
( details ? . info === undefined ) ? null :
cssNote ( details . info ) ,
( result . details === undefined ) ? null :
Object . entries ( result . details ) . map ( ( [ key , val ] ) = > {
return dom (
'div' ,
cssLabel ( key ) ,
dom ( 'input' , dom . prop ( 'value' , JSON . stringify ( val ) ) ) )
} ) ,
] ,
} ) ;
}
}
function maybeSwitchToggle ( value : Observable < boolean | null > ) : DomContents {
return toggle ( value , dom . hide ( ( use ) = > use ( value ) === null ) ) ;
}
const cssNote = styled ( 'div' , `
border - left : 2px solid $ { theme . lightText } ;
padding - left : 5px ;
padding - bottom : 5px ;
padding - top : 5px ;
margin - bottom : 5px ;
` );
const cssStatus = styled ( 'div' , `
display : inline - block ;
text - align : center ;
width : 40px ;
padding : 5px ;
` );
const cssPageContainer = styled ( 'div' , `
overflow : auto ;
padding : 40px ;
@ -598,3 +707,10 @@ export const cssError = styled('div', `
export const cssHappy = styled ( 'div' , `
color : $ { theme . controlFg } ;
` );
export const cssLabel = styled ( 'div' , `
display : inline - block ;
min - width : 100px ;
text - align : right ;
padding - right : 5px ;
` );