'use strict'; import React, { Component } from 'react'; import { translate } from 'react-i18next'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom' import './page.css'; import { withErrorHandling } from './error-handling'; import interoperableErrors from '../../../shared/interoperable-errors'; import { DismissibleAlert, Button } from './bootstrap-components'; import mailtrainConfig from 'mailtrainConfig'; class PageContent extends Component { static propTypes = { structure: PropTypes.object.isRequired } getRoutes(urlPrefix, children) { let routes = []; for (let routeKey in children) { const structure = children[routeKey]; let path = urlPrefix + routeKey; let pathWithParams = path; if (structure.params) { pathWithParams = pathWithParams + '/' + structure.params.join('/'); } if (structure.component || structure.render) { const route = { component: structure.component, render: structure.render, path: (pathWithParams === '' ? '/' : pathWithParams) }; routes.push(route); } if (structure.children) { routes = routes.concat(this.getRoutes(path + '/', structure.children)); } } return routes; } renderRoute(route) { if (route.component) { return ; } else if (route.render) { return ; } } render() { let routes = this.getRoutes('', this.props.structure); return {routes.map(x => this.renderRoute(x))}; } } @withRouter class Breadcrumb extends Component { static propTypes = { structure: PropTypes.object.isRequired } renderElement(breadcrumbElem) { if (breadcrumbElem.isActive) { return
  • {breadcrumbElem.title}
  • ; } else if (breadcrumbElem.externalLink) { return
  • {breadcrumbElem.title}
  • ; } else if (breadcrumbElem.link) { let link; if (typeof breadcrumbElem.link === 'function') { link = breadcrumbElem.link(this.props.match); } else { link = breadcrumbElem.link; } return
  • {breadcrumbElem.title}
  • ; } else { return
  • {breadcrumbElem.title}
  • ; } } render() { const location = this.props.location.pathname; const locationElems = location.split('/'); let breadcrumbElems = []; let children = this.props.structure; for (let idx = 0; idx < locationElems.length; idx++) { const breadcrumbElem = children[locationElems[idx]]; if (!breadcrumbElem) { break; } breadcrumbElem.isActive = (idx === locationElems.length - 1); breadcrumbElem.idx = idx; breadcrumbElems.push(breadcrumbElem); children = breadcrumbElem.children; if (!children) { break; } } const renderedElems = breadcrumbElems.map(x => this.renderElement(x)); return
      {renderedElems}
    ; } } @withRouter @withErrorHandling class SectionContent extends Component { constructor(props) { super(props); this.state = { flashMessageText: '' } this.historyUnlisten = props.history.listen((location, action) => { this.closeFlashMessage(); }) // ------------------------------------------------------------------------------------------------------- /* FIXME - remove this once we migrate fully to React This part transforms the flash notice rendered by the server to flash notice managed by React client. It is used primarily for the login info, but there may be some other cases. */ const alrt = jQuery('.container>.alert'); alrt.find('button').remove(); const alrtText = alrt.text(); if (alrtText) { this.state.flashMessageText = alrtText; const severityRegex = /alert-([^ ]*)/; const match = alrt.attr('class').match(severityRegex); if (match) { this.state.flashMessageSeverity = match[1]; } } alrt.remove(); // ------------------------------------------------------------------------------------------------------- } static propTypes = { structure: PropTypes.object.isRequired, root: PropTypes.string.isRequired } static childContextTypes = { sectionContent: PropTypes.object } getChildContext() { return { sectionContent: this }; } getFlashMessageText() { return this.state.flashMessageText; } getFlashMessageSeverity() { return this.state.flashMessageSeverity; } setFlashMessage(severity, text) { this.setState({ flashMessageText: text, flashMessageSeverity: severity }); } navigateTo(path) { this.props.history.push(path); } navigateBack() { this.props.history.goBack(); } navigateToWithFlashMessage(path, severity, text) { this.props.history.push(path); this.setFlashMessage(severity, text); } ensureAuthenticated() { if (!mailtrainConfig.isAuthenticated) { /* FIXME, once we turn Mailtrain to single-page application, this should become navigateTo */ window.location = '/account/login?next=' + encodeURIComponent(this.props.root); } } errorHandler(error) { if (error instanceof interoperableErrors.NotLoggedInError) { /* FIXME, once we turn Mailtrain to single-page application, this should become navigateTo */ window.location = '/account/login?next=' + encodeURIComponent(this.props.root); } else if (error.response && error.response.data && error.response.data.message) { console.error(error); this.navigateToWithFlashMessage(this.props.root, 'danger', error.response.data.message); } else { console.error(error); this.navigateToWithFlashMessage(this.props.root, 'danger', error.message); } return true; } async closeFlashMessage() { this.setState({ flashMessageText: '' }) } render() { return (
    {(this.state.flashMessageText && {this.state.flashMessageText})}
    ); } } @translate() class Section extends Component { constructor(props) { super(props); let structure = props.structure; if (typeof structure === 'function') { structure = structure(props.t); } this.structure = structure; } static propTypes = { structure: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired, root: PropTypes.string.isRequired } render() { return ( ); } } class Title extends Component { render() { return (

    {this.props.children}


    ); } } class Toolbar extends Component { render() { return (
    {this.props.children}
    ); } } class NavButton extends Component { static propTypes = { label: PropTypes.string, icon: PropTypes.string, className: PropTypes.string, linkTo: PropTypes.string }; render() { const props = this.props; return (