Skip to content
1 change: 1 addition & 0 deletions frontend/api/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frontend/common/stores/base/_store.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ module.exports = Object.assign({}, EventEmitter.prototype, {

isSaving: false,

loaded() {
loaded(persistError = false) {
this.hasLoaded = true
this.error = null
this.error = persistError ? this.error : null
this.isLoading = false
this.trigger(DEFAULT_LOADED_EVENT)
this.trigger(DEFAULT_CHANGE_EVENT)
Expand Down
9 changes: 8 additions & 1 deletion frontend/common/stores/config-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ const controller = {
store.model = flagsmith.getAllFlags()
},
onError() {
store.error = true
if (Project.isFlagsmithOnFlagsmith) {
store.model = {}
// TODO: Migrate to TS and use enum
store.error = 'fof_init_error'
store.loaded(true)
return
}
store.error = 'unknown'
store.goneABitWest()
},
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ const Utils = Object.assign({}, require('./base/_utils'), {
* only add behaviour to Flagsmith-on-Flagsmith flags that have been explicitly created by customers.
*/
flagsmithFeatureExists(flag: string) {
return Object.prototype.hasOwnProperty.call(flagsmith.getAllFlags(), flag)
const allFlags = flagsmith?.getAllFlags()
return allFlags && Object.prototype.hasOwnProperty.call(allFlags, flag)
},
getContentType(contentTypes: ContentType[], model: string, type: string) {
return contentTypes.find((c: ContentType) => c[model] === type) || null
Expand Down
1 change: 1 addition & 0 deletions frontend/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ declare global {
theme?: string,
expiry?: number,
action?: { buttonText: string; onClick: () => void },
position?: 'top' | 'bottom',
) => void
const Flex: typeof Component
const isMobile: boolean
Expand Down
30 changes: 24 additions & 6 deletions frontend/web/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ import { Provider } from 'react-redux'
import { getStore } from 'common/store'
import { resolveAuthFlow } from '@datadog/ui-extensions-sdk'
import ConfigProvider from 'common/providers/ConfigProvider'
import Button from './base/forms/Button'
import Icon from './Icon'
import AccountStore from 'common/stores/account-store'
import OrganisationLimit from './OrganisationLimit'
import GithubStar from './GithubStar'
import Tooltip from './Tooltip'
import classNames from 'classnames'
import { apps, gitBranch, gitCompare, statsChart } from 'ionicons/icons'
import {
getStartupErrorText,
isFlagsmithOnFlagsmithError,
isMaintenanceError,
} from './base/errors/init.error'
import NavSubLink from './navigation/NavSubLink'
import SettingsIcon from './svg/SettingsIcon'
import UsersIcon from './svg/UsersIcon'
Expand Down Expand Up @@ -304,9 +307,24 @@ const App = class extends Component {
) {
return <Blocked />
}
if (Project.maintenance || this.props.error || !window.projectOverrides) {
const maintenanceMode =
Utils.getFlagsmithHasFeature('maintenance_mode') || Project.maintenance
const isUnknownError =
this.props.error && !isFlagsmithOnFlagsmithError(this.props.error)
if (maintenanceMode || !window.projectOverrides || isUnknownError) {
return <Maintenance />
}

if (this.props.error && isFlagsmithOnFlagsmithError(this.props.error)) {
toast(
getStartupErrorText(this.props.error),
'danger',
2 * 60 * 1000,
undefined,
'top',
)
}

const activeProject = OrganisationStore.getProject(projectId)
const projectNotLoaded =
!activeProject && document.location.href.includes('project/')
Expand All @@ -326,20 +344,20 @@ const App = class extends Component {
</AccountProvider>
)
}

if (AccountStore.forced2Factor()) {
return <AccountSettingsPage isLoginPage={true} />
}

if (document.location.pathname.includes('widget')) {
return <div>{this.props.children}</div>
}
const isOrganisationSelect = document.location.pathname === '/organisations'
const integrations = Object.keys(Utils.getIntegrationData())
const environmentMetricsEnabled = Utils.getFlagsmithHasFeature(
'environment_metrics',
)
const projectMetricsTooltipEnabled = Utils.getFlagsmithHasFeature(
'project_metrics_tooltip',
)

return (
<Provider store={getStore()}>
<AccountProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
import React from 'react'
import ConfigProvider from 'common/providers/ConfigProvider'

const HomePage = class extends React.Component {
static displayName = 'HomePage'

constructor(props, context) {
super(props, context)
this.state = {}
}

render = () => (
const MaintenancePage: React.FC = () => {
return (
<div className='fullscreen-container maintenance justify-content-center'>
<div className='col-md-6 mt-5' id='sign-up'>
<h1>Maintenance</h1>
We are currently undergoing some scheduled maintenance of the admin
site, this will not affect your application's feature flags.
{
<>
{' '}
Check{' '}
<a
target='_blank'
href='https://x.com/getflagsmith'
rel='noreferrer'
>
@getflagsmith
</a>{' '}
for updates.
</>
}
<>
{' '}
Check{' '}
<a target='_blank' href='https://x.com/getflagsmith' rel='noreferrer'>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not fundamental part of this PR but we should consider replacing this with https://status.flagsmith.com/ because we're not posting status updates on twitter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely agree with this, but let's include this in a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<a target='_blank' href='https://x.com/getflagsmith' rel='noreferrer'>
<a target='_blank' href='https://status.flagsmith.com/' rel='noreferrer'>

@getflagsmith
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@getflagsmith
Flagsmith status

</a>{' '}
for updates.
</>
<br />
<p className='small'>
Sorry for the inconvenience, we will be back up and running shortly.
Expand All @@ -38,4 +25,6 @@ const HomePage = class extends React.Component {
)
}

module.exports = ConfigProvider(HomePage)
MaintenancePage.displayName = 'MaintenancePage'

export default ConfigProvider(MaintenancePage)
25 changes: 25 additions & 0 deletions frontend/web/components/base/errors/init.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export enum FlagsmithStartupErrors {
FOF_INIT_ERROR = 'fof_init_error',
UNKNOWN_ERROR = 'unknown_error',
MAINTENANCE_MODE = 'maintenance_mode',
}

const errorLabels: Record<FlagsmithStartupErrors, string> = {
[FlagsmithStartupErrors.FOF_INIT_ERROR]:
'Flagsmith on Flagsmith initialisation error. Please check your configuration',
[FlagsmithStartupErrors.MAINTENANCE_MODE]: 'Maintenance mode',
[FlagsmithStartupErrors.UNKNOWN_ERROR]:
'Unexpected error. If it persists, please contact support',
}

export const getStartupErrorText = (error: FlagsmithStartupErrors) => {
return errorLabels[error] || errorLabels[FlagsmithStartupErrors.UNKNOWN_ERROR]
}

export const isMaintenanceError = (error: FlagsmithStartupErrors) => {
return error === FlagsmithStartupErrors.MAINTENANCE_MODE
}

export const isFlagsmithOnFlagsmithError = (error: FlagsmithStartupErrors) => {
return error === FlagsmithStartupErrors.FOF_INIT_ERROR
}
Loading