2022-10-28 16:11:08 +00:00
import { makeT } from 'app/client/lib/localization' ;
2020-10-02 15:10:00 +00:00
import { AppModel } from 'app/client/models/AppModel' ;
2022-05-18 10:25:14 +00:00
import { getLoginUrl , getMainOrgUrl , urlState } from 'app/client/models/gristUrlState' ;
2021-08-18 17:49:34 +00:00
import { AppHeader } from 'app/client/ui/AppHeader' ;
2020-10-02 15:10:00 +00:00
import { leftPanelBasic } from 'app/client/ui/LeftPanelCommon' ;
import { pagePanels } from 'app/client/ui/PagePanels' ;
import { createTopBarHome } from 'app/client/ui/TopBar' ;
import { bigBasicButtonLink , bigPrimaryButtonLink } from 'app/client/ui2018/buttons' ;
2022-09-06 01:51:57 +00:00
import { theme , vars } from 'app/client/ui2018/cssVars' ;
2022-06-10 19:13:11 +00:00
import { getPageTitleSuffix , GristLoadConfig } from 'app/common/gristUrls' ;
import { getGristConfig } from 'app/common/urlUtils' ;
2020-10-02 15:10:00 +00:00
import { dom , DomElementArg , makeTestId , observable , styled } from 'grainjs' ;
const testId = makeTestId ( 'test-' ) ;
2022-10-28 16:11:08 +00:00
const t = makeT ( 'errorPages' ) ;
2020-10-02 15:10:00 +00:00
export function createErrPage ( appModel : AppModel ) {
const gristConfig : GristLoadConfig = ( window as any ) . gristConfig || { } ;
const message = gristConfig . errMessage ;
return gristConfig . errPage === 'signed-out' ? createSignedOutPage ( appModel ) :
gristConfig . errPage === 'not-found' ? createNotFoundPage ( appModel , message ) :
gristConfig . errPage === 'access-denied' ? createForbiddenPage ( appModel , message ) :
createOtherErrorPage ( appModel , message ) ;
}
/ * *
* Creates a page to show that the user has no access to this org .
* /
export function createForbiddenPage ( appModel : AppModel , message? : string ) {
2022-12-06 13:57:29 +00:00
document . title = t ( "Access denied{{suffix}}" , { suffix : getPageTitleSuffix ( getGristConfig ( ) ) } ) ;
2022-06-10 19:13:11 +00:00
2022-05-18 10:25:14 +00:00
const isAnonym = ( ) = > ! appModel . currentValidUser ;
const isExternal = ( ) = > appModel . currentValidUser ? . loginMethod === 'External' ;
2022-12-06 13:57:29 +00:00
return pagePanelsError ( appModel , t ( "Access denied{{suffix}}" , { suffix : '' } ) , [
2020-10-02 15:10:00 +00:00
dom . domComputed ( appModel . currentValidUser , user = > user ? [
2022-12-06 13:57:29 +00:00
cssErrorText ( message || t ( "You do not have access to this organization's documents." ) ) ,
cssErrorText ( t ( "You are signed in as {{email}}. You can sign in with a different account, or ask an administrator for access." , { email : dom ( 'b' , user . email ) } ) ) , // TODO: i18next
2020-10-02 15:10:00 +00:00
] : [
// This page is not normally shown because a logged out user with no access will get
// redirected to log in. But it may be seen if a user logs out and returns to a cached
2022-05-18 10:25:14 +00:00
// version of this page or is an external user (connected through GristConnect).
2022-12-06 13:57:29 +00:00
cssErrorText ( t ( "Sign in to access this organization's documents." ) ) ,
2020-10-02 15:10:00 +00:00
] ) ,
cssButtonWrap ( bigPrimaryButtonLink (
2022-12-06 13:57:29 +00:00
isExternal ( ) ? t ( "Go to main page" ) :
isAnonym ( ) ? t ( "Sign in" ) :
t ( "Add account" ) ,
2022-05-18 10:25:14 +00:00
{ href : isExternal ( ) ? getMainOrgUrl ( ) : getLoginUrl ( ) } ,
2020-10-02 15:10:00 +00:00
testId ( 'error-signin' ) ,
) )
] ) ;
}
/ * *
* Creates a page that shows the user is logged out .
* /
export function createSignedOutPage ( appModel : AppModel ) {
2022-12-06 13:57:29 +00:00
document . title = t ( "Signed out{{suffix}}" , { suffix : getPageTitleSuffix ( getGristConfig ( ) ) } ) ;
2022-06-10 19:13:11 +00:00
2022-12-06 13:57:29 +00:00
return pagePanelsError ( appModel , t ( "Signed out{{suffix}}" , { suffix : '' } ) , [
cssErrorText ( t ( "You are now signed out." ) ) ,
2020-10-02 15:10:00 +00:00
cssButtonWrap ( bigPrimaryButtonLink (
2022-12-06 13:57:29 +00:00
t ( "Sign in again" ) , { href : getLoginUrl ( ) } , testId ( 'error-signin' )
2020-10-02 15:10:00 +00:00
) )
] ) ;
}
/ * *
* Creates a "Page not found" page .
* /
export function createNotFoundPage ( appModel : AppModel , message? : string ) {
2022-12-06 13:57:29 +00:00
document . title = t ( "Page not found{{suffix}}" , { suffix : getPageTitleSuffix ( getGristConfig ( ) ) } ) ;
2022-06-10 19:13:11 +00:00
2022-12-06 13:57:29 +00:00
return pagePanelsError ( appModel , t ( "Page not found{{suffix}}" , { suffix : '' } ) , [
cssErrorText ( message || t ( "The requested page could not be found.{{separator}}Please check the URL and try again." , { separator : dom ( 'br' ) } ) ) , // TODO: i18next
cssButtonWrap ( bigPrimaryButtonLink ( t ( "Go to main page" ) , testId ( 'error-primary-btn' ) ,
2020-10-02 15:10:00 +00:00
urlState ( ) . setLinkUrl ( { } ) ) ) ,
2022-12-06 13:57:29 +00:00
cssButtonWrap ( bigBasicButtonLink ( t ( "Contact support" ) , { href : 'https://getgrist.com/contact' } ) ) ,
2020-10-02 15:10:00 +00:00
] ) ;
}
/ * *
* Creates a generic error page with the given message .
* /
export function createOtherErrorPage ( appModel : AppModel , message? : string ) {
2022-12-06 13:57:29 +00:00
document . title = t ( "Error{{suffix}}" , { suffix : getPageTitleSuffix ( getGristConfig ( ) ) } ) ;
2022-06-10 19:13:11 +00:00
2022-12-06 13:57:29 +00:00
return pagePanelsError ( appModel , t ( "Something went wrong" ) , [
2022-12-06 15:36:14 +00:00
cssErrorText ( message ? t ( 'There was an error: {{message}}' , { message : addPeriod ( message ) } ) :
t ( 'There was an unknown error.' ) ) ,
2022-12-06 13:57:29 +00:00
cssButtonWrap ( bigPrimaryButtonLink ( t ( "Go to main page" ) , testId ( 'error-primary-btn' ) ,
2020-10-02 15:10:00 +00:00
urlState ( ) . setLinkUrl ( { } ) ) ) ,
2022-12-06 13:57:29 +00:00
cssButtonWrap ( bigBasicButtonLink ( t ( "Contact support" ) , { href : 'https://getgrist.com/contact' } ) ) ,
2020-10-02 15:10:00 +00:00
] ) ;
}
function addPeriod ( msg : string ) : string {
return msg . endsWith ( '.' ) ? msg : msg + '.' ;
}
function pagePanelsError ( appModel : AppModel , header : string , content : DomElementArg ) {
const panelOpen = observable ( false ) ;
return pagePanels ( {
leftPanel : {
panelWidth : observable ( 240 ) ,
panelOpen ,
hideOpener : true ,
2021-08-18 17:49:34 +00:00
header : dom.create ( AppHeader , appModel . currentOrgName , appModel ) ,
2020-10-02 15:10:00 +00:00
content : leftPanelBasic ( appModel , panelOpen ) ,
} ,
headerMain : createTopBarHome ( appModel ) ,
contentMain : cssCenteredContent ( cssErrorContent (
cssBigIcon ( ) ,
cssErrorHeader ( header , testId ( 'error-header' ) ) ,
content ,
testId ( 'error-content' ) ,
) ) ,
} ) ;
}
const cssCenteredContent = styled ( 'div' , `
width : 100 % ;
height : 100 % ;
overflow - y : auto ;
` );
const cssErrorContent = styled ( 'div' , `
text - align : center ;
margin : 64px 0 64 px ;
` );
const cssBigIcon = styled ( 'div' , `
display : inline - block ;
width : 100 % ;
height : 64px ;
background - image : var ( -- icon - GristLogo ) ;
background - size : contain ;
background - repeat : no - repeat ;
background - position : center ;
` );
const cssErrorHeader = styled ( 'div' , `
font - weight : $ { vars . headerControlTextWeight } ;
font - size : $ { vars . xxxlargeFontSize } ;
margin : 24px ;
text - align : center ;
2022-09-06 01:51:57 +00:00
color : $ { theme . text } ;
2020-10-02 15:10:00 +00:00
` );
const cssErrorText = styled ( 'div' , `
font - size : $ { vars . mediumFontSize } ;
2022-09-06 01:51:57 +00:00
color : $ { theme . text } ;
2020-10-02 15:10:00 +00:00
margin : 0 auto 24 px auto ;
max - width : 400px ;
text - align : center ;
` );
const cssButtonWrap = styled ( 'div' , `
margin - bottom : 8px ;
` );