'use strict'; import React, {Component} from "react"; import {withTranslation} from './i18n'; import PropTypes from "prop-types"; import {withRouter} from "react-router"; import { BrowserRouter as Router, Link, Redirect, Route, Switch } from "react-router-dom"; import { withAsyncErrorHandler, withErrorHandling } from "./error-handling"; import interoperableErrors from "../../../shared/interoperable-errors"; import { Button, DismissibleAlert, Icon } from "./bootstrap-components"; import mailtrainConfig from "mailtrainConfig"; import styles from "./styles.scss"; import { getRoutes, needsResolve, resolve, SectionContentContext, withPageHelpers } from "./page-common"; import {getBaseDir} from "./urls"; import { createComponentMixin, withComponentMixins } from "./decorator-helpers"; export { withPageHelpers } class Breadcrumb extends Component { constructor(props) { super(props); } static propTypes = { route: PropTypes.object.isRequired, params: PropTypes.object.isRequired, resolved: PropTypes.object.isRequired } renderElement(entry, isActive) { const params = this.props.params; let title; if (typeof entry.title === 'function') { title = entry.title(this.props.resolved, params); } else { title = entry.title; } if (isActive) { return
  • {title}
  • ; } else if (entry.externalLink) { let externalLink; if (typeof entry.externalLink === 'function') { externalLink = entry.externalLink(params); } else { externalLink = entry.externalLink; } return
  • {title}
  • ; } else if (entry.link) { let link; if (typeof entry.link === 'function') { link = entry.link(params); } else { link = entry.link; } return
  • {title}
  • ; } else { return
  • {title}
  • ; } } render() { const route = this.props.route; const renderedElems = [...route.parents.map(x => this.renderElement(x)), this.renderElement(route, true)]; return ; } } //TODO class SecondaryNavBar extends Component { static propTypes = { route: PropTypes.object.isRequired, params: PropTypes.object.isRequired, resolved: PropTypes.object.isRequired, className: PropTypes.string } renderElement(key, entry) { const params = this.props.params; let title; if (typeof entry.title === 'function') { title = entry.title(this.props.resolved); } else { title = entry.title; } let className = ''; if (entry.active) { className += ' active'; } if (entry.link) { let link; if (typeof entry.link === 'function') { link = entry.link(params); } else { link = entry.link; } return
  • {title}
  • ; } else if (entry.externalLink) { let externalLink; if (typeof entry.externalLink === 'function') { externalLink = entry.externalLink(params); } else { externalLink = entry.externalLink; } return
  • {title}
  • ; } else { return
  • {title}
  • ; } } render() { const route = this.props.route; const keys = Object.keys(route.navs); const renderedElems = []; for (const key in keys) { const entry = route.navs[key]; let visible = true; if (typeof entry.visible === 'function') { visible = entry.visible(this.props.resolved); } if (visible) { renderedElems.push(this.renderElement(key, entry)); } } if (renderedElems.length > 1) { let className = styles.secondaryNav + ' nav nav-pills'; if (this.props.className) { className += ' ' + this.props.className; } return ; } else { return null; } } } @withComponentMixins([ withTranslation, withErrorHandling ]) class RouteContent extends Component { constructor(props) { super(props); this.state = {}; if (Object.keys(props.route.resolve).length === 0) { this.state.resolved = {}; } } static propTypes = { route: PropTypes.object.isRequired, flashMessage: PropTypes.object } @withAsyncErrorHandler async resolve(props) { if (Object.keys(props.route.resolve).length === 0) { this.setState({ resolved: {} }); } else { this.setState({ resolved: null }); const resolved = await resolve(props.route, props.match); if (!this.disregardResolve) { // This is to prevent the warning about setState on discarded component when we immediatelly redirect. this.setState({ resolved }); } } } componentDidMount() { // noinspection JSIgnoredPromiseFromCall this.resolve(this.props); } componentDidUpdate() { } componentWillReceiveProps(nextProps) { if (this.props.match.params !== nextProps.match.params && needsResolve(this.props.route, nextProps.route, this.props.match, nextProps.match)) { // noinspection JSIgnoredPromiseFromCall this.resolve(nextProps); } } componentWillUnmount() { this.disregardResolve = true; // This is to prevent the warning about setState on discarded component when we immediatelly redirect. } render() { const t = this.props.t; const route = this.props.route; const params = this.props.match.params; const resolved = this.state.resolved; if (!route.panelRender && !route.panelComponent && route.link) { let link; if (typeof route.link === 'function') { link = route.link(params); } else { link = route.link; } return ; } else { const primaryMenuProps = { location: this.props.location }; const primaryMenuComponent = React.createElement(route.primaryMenuComponent, primaryMenuProps); if (resolved) { const compProps = { match: this.props.match, location: this.props.location, resolved }; let panel; if (route.panelComponent) { panel = React.createElement(route.panelComponent, compProps); } else if (route.panelRender) { panel = route.panelRender(compProps); } return (
    {primaryMenuComponent}
    {this.props.flashMessage} {panel}
    ); } else { return (
    {primaryMenuComponent}
    {t('loading')}
    ); } } } } @withRouter @withComponentMixins([ withErrorHandling ]) export class SectionContent extends Component { constructor(props) { super(props); this.state = { } this.historyUnlisten = props.history.listen((location, action) => { // noinspection JSIgnoredPromiseFromCall this.closeFlashMessage(); }) } static propTypes = { structure: PropTypes.object.isRequired, root: PropTypes.string.isRequired } 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) { this.navigateTo('/account/login?next=' + encodeURIComponent(window.location.pathname)); } } errorHandler(error) { if (error instanceof interoperableErrors.NotLoggedInError) { if (window.location.pathname !== '/account/login') { // There may be multiple async requests failing at the same time. So we take the pathname only from the first one. this.navigateTo('/account/login?next=' + encodeURIComponent(window.location.pathname)); } } 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: '' }); } renderRoute(route) { let flashMessage; if (this.state.flashMessageText) { flashMessage = {this.state.flashMessageText}; } const render = props => ; return } render() { let routes = getRoutes('', {}, [], this.props.structure, [], null, null); return ( {routes.map(x => this.renderRoute(x))} ); } } @withComponentMixins([ withTranslation ]) export class Section extends Component { constructor(props) { super(props); } static propTypes = { structure: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired, root: PropTypes.string.isRequired } render() { let structure = this.props.structure; if (typeof structure === 'function') { structure = structure(this.props.t); } return ( ); } } export class Title extends Component { render() { return (

    {this.props.children}


    ); } } export class Toolbar extends Component { static propTypes = { className: PropTypes.string, }; render() { let className = 'float-right ' + styles.buttonRow; if (this.props.className) { className += ' ' + this.props.className; } return (
    {this.props.children}
    ); } } export class NavButton extends Component { static propTypes = { label: PropTypes.string, icon: PropTypes.string, className: PropTypes.string, linkTo: PropTypes.string }; render() { const props = this.props; return (