diff --git a/client/src/campaigns/CUD.js b/client/src/campaigns/CUD.js index 792a17d2..6c81937d 100644 --- a/client/src/campaigns/CUD.js +++ b/client/src/campaigns/CUD.js @@ -229,7 +229,6 @@ export default class CUD extends Component { [lstPrefix + 'list']: null, [lstPrefix + 'segment']: null, [lstPrefix + 'useSegmentation']: false, - [lstPrefix + 'work_list_current_namespace']: false, lists: [lstUid], send_configuration: null, @@ -237,8 +236,6 @@ export default class CUD extends Component { click_tracking_disabled: false, open_tracking_disabled: false, - work_send_config_current_namespace: false, - work_template_current_namespace: false, unsubscribe_url: '', @@ -524,18 +521,12 @@ export default class CUD extends Component { const lstsEditEntries = []; const lsts = this.getFormValue('lists') || []; let lstOrderIdx = 0; - - const currentNamespace = this.getFormValue('namespace'); - const useNamespaceSendConfig = this.getFormValue('work_send_config_current_namespace'); - const useNamespaceTemplate = this.getFormValue('work_template_current_namespace'); - for (const lstUid of lsts) { const prefix = 'lists_' + lstUid + '_'; const lstOrderIdxClosure = lstOrderIdx; const selectedList = this.getFormValue(prefix + 'list'); - const useNamespaceLists = this.getFormValue(prefix + 'work_list_current_namespace'); - + lstsEditEntries.push(
@@ -571,13 +562,8 @@ export default class CUD extends Component { }
- - {useNamespaceLists && - - } - {!useNamespaceLists && - - } + + {(campaignTypeKey === CampaignType.REGULAR || campaignTypeKey === CampaignType.RSS) &&
@@ -622,7 +608,7 @@ export default class CUD extends Component { sendSettings = []; const addOverridable = (id, label) => { - if(this.state.sendConfiguration[id + '_overridable'] == 1){ + if(this.state.sendConfiguration[id + '_overridable']){ if (this.getFormValue(id + '_overriden')) { sendSettings.push(); } else { @@ -632,7 +618,7 @@ export default class CUD extends Component { ); } - sendSettings.push(); + sendSettings.push(); } else{ sendSettings.push( @@ -681,13 +667,7 @@ export default class CUD extends Component { // The "key" property here and in the TableSelect below is to tell React that these tables are different and should be rendered by different instances. Otherwise, React will use // only one instance, which fails because Table does not handle updates in "columns" property - if(useNamespaceTemplate){ - templateEdit = ; - } - else{ - templateEdit = ; - } - + templateEdit = ; } else if (!isEdit && sourceTypeKey === CampaignSource.CUSTOM_FROM_CAMPAIGN) { const campaignsColumns = [ @@ -759,32 +739,34 @@ export default class CUD extends Component { {lstsEdit} - {t('sendSettings')} +
- +
- {useNamespaceSendConfig && - - } - {!useNamespaceSendConfig && - - } - - - {sendSettings} - - + + + {sendSettings} + + +

- - +
+ + +
- {t('template')} + {sourceEdit && + <> +
+
+ {sourceEdit} +
+ + } - {sourceEdit} - {templateEdit} diff --git a/client/src/campaigns/styles.scss b/client/src/campaigns/styles.scss index a1a05000..c8b29d41 100644 --- a/client/src/campaigns/styles.scss +++ b/client/src/campaigns/styles.scss @@ -1,92 +1,94 @@ -.entry { - border-bottom: 1px solid #e5e5e5; - margin-bottom: 15px; - min-height: 91px; - position: relative; - - &:last-child { - border-bottom: 0px none; - margin-bottom: 0px; - } - - .entryButtons { - position: absolute; - top: -8px; - right: -8px; - width: 19px; - - button { - padding: 2px 3px; - font-size: 11px; - display: block; - margin-bottom: 2px; - } - - button:last-child { - margin-bottom: 0px; - } - } - - &.entryWithButtons > .entryContent { - margin-right: 26px; - } -} - -.newEntry { - text-align: right; - margin-bottom: 15px; -} - -.sendButtonRow { - margin-top: 10px; -} - -.statsMetrics { - width: 10ex; - display: inline-block; -} - -.statsProgressBar { - margin-right: 30px; - margin-bottom: 0px; -} - -.statsProgressBarZoomIn { - float: right; - width: 30px; - text-align: right; - display: block; -} - -.zoomIn { - padding-left: 15px; -} - -.navPills { - margin-top: -3px; - margin-bottom: 5px; - float: right; - - & > li { - display: inline-block; - float: none; - - & > a { - padding: 3px 10px; - } - } -} - -.charts { - margin-bottom: 30px; - - .chart { - margin-bottom: 30px; - } -} - -.sectionTitle { - margin-bottom: 30px; -} - - +.entry { + border-bottom: 1px solid #e5e5e5; + margin-bottom: 15px; + min-height: 91px; + position: relative; + + &:last-child { + border-bottom: 0px none; + margin-bottom: 0px; + } + + .entryButtons { + position: absolute; + top: -8px; + right: -8px; + width: 19px; + + button { + padding: 2px 3px; + font-size: 11px; + display: block; + margin-bottom: 2px; + } + + button:last-child { + margin-bottom: 0px; + } + } + + &.entryWithButtons > .entryContent { + margin-right: 26px; + } +} + +.newEntry { + text-align: right; + margin-bottom: 15px; +} + +.sendButtonRow { + margin-top: 10px; +} + +.statsMetrics { + width: 10ex; + display: inline-block; +} + +.statsProgressBar { + margin-right: 30px; + margin-bottom: 0px; +} + +.statsProgressBarZoomIn { + float: right; + width: 30px; + text-align: right; + display: block; +} + +.zoomIn { + padding-left: 15px; +} + +.navPills { + margin-top: -3px; + margin-bottom: 5px; + float: right; + + & > li { + display: inline-block; + float: none; + + & > a { + padding: 3px 10px; + } + } +} + +.charts { + margin-bottom: 30px; + + .chart { + margin-bottom: 30px; + } +} + +.sectionTitle { + margin-bottom: 30px; +} + +.overrideCheckbox{ + margin-top: -8px !important; +} diff --git a/client/src/lib/form.js b/client/src/lib/form.js index 85c9a2d5..747a4801 100644 --- a/client/src/lib/form.js +++ b/client/src/lib/form.js @@ -340,7 +340,7 @@ class CheckBox extends Component { label: PropTypes.string, help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), format: PropTypes.string, - overrideFormat: PropTypes.bool + className: PropTypes.string } render() { @@ -348,28 +348,15 @@ class CheckBox extends Component { const owner = this.getFormStateOwner(); const id = this.props.id; const htmlId = 'form_' + id; - const overrideFormat = this.props.overrideFormat; - const className = owner.addFormValidationClass('form-check-input', id); + const inputClassName = owner.addFormValidationClass('form-check-input', id); - if(overrideFormat){ - return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help, -
- owner.updateFormValue(id, !owner.getFormValue(id))}/> + return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help, +
+ owner.updateFormValue(id, !owner.getFormValue(id))}/>
- ); - } - else{ - return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help, -
- owner.updateFormValue(id, !owner.getFormValue(id))}/> - -
- ); - } - - + ); } } diff --git a/client/src/lib/page.js b/client/src/lib/page.js index 7de90d0f..995b58d7 100644 --- a/client/src/lib/page.js +++ b/client/src/lib/page.js @@ -1,701 +1,699 @@ -'use strict'; - -import React, {Component} from "react"; -import i18n, {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 { - ActionLink, - Button, - DismissibleAlert, - DropdownActionLink, - ButtonDropdown, - 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"; -import {getLang} from "../../../shared/langs"; - -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 ; - } -} - -class TertiaryNavBar 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 liClassName = 'nav-item'; - let linkClassName = 'nav-link'; - if (entry.active) { - linkClassName += ' 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 of 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.tertiaryNav + ' nav nav-pills'; - if (this.props.className) { - className += ' ' + this.props.className; - } - - return
      {renderedElems}
    ; - } else { - return null; - } - } -} - -@withComponentMixins([ - withTranslation, - withErrorHandling -]) -class RouteContent extends Component { - constructor(props) { - super(props); - this.state = { - panelInFullScreen: props.route.panelInFullScreen - }; - - if (Object.keys(props.route.resolve).length === 0) { - this.state.resolved = {}; - } - - this.sidebarAnimationNodeListener = evt => { - if (evt.propertyName === 'left') { - this.forceUpdate(); - } - }; - - this.setPanelInFullScreen = panelInFullScreen => this.setState({ panelInFullScreen }); - } - - static propTypes = { - route: PropTypes.object.isRequired, - flashMessage: PropTypes.object - } - - @withAsyncErrorHandler - async resolve() { - const props = this.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 - }); - } - } - } - - registerSidebarAnimationListener() { - if (this.sidebarAnimationNode) { - this.sidebarAnimationNode.addEventListener("transitionend", this.sidebarAnimationNodeListener); - } - } - - componentDidMount() { - // noinspection JSIgnoredPromiseFromCall - this.resolve(); - this.registerSidebarAnimationListener(); - } - - componentDidUpdate(prevProps) { - this.registerSidebarAnimationListener(); - - if (this.props.location.state !== prevProps.location.state || (this.props.match.params !== prevProps.match.params && needsResolve(prevProps.route, this.props.route, prevProps.match, this.props.match))) { - // noinspection JSIgnoredPromiseFromCall - this.resolve(); - } - } - - 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; - - const showSidebar = !!route.secondaryMenuComponent; - - const panelInFullScreen = this.state.panelInFullScreen; - - if (!route.panelRender && !route.panelComponent && route.link) { - let link; - if (typeof route.link === 'function') { - link = route.link(params); - } else { - link = route.link; - } - - return ; - - } else { - let primaryMenu = null; - let secondaryMenu = null; - let content = null; - - if (resolved) { - const compProps = { - match: this.props.match, - location: this.props.location, - resolved, - setPanelInFullScreen: this.setPanelInFullScreen, - panelInFullScreen: this.state.panelInFullScreen - }; - - let panel; - if (route.panelComponent) { - panel = React.createElement(route.panelComponent, compProps); - } else if (route.panelRender) { - panel = route.panelRender(compProps); - } - - if (route.primaryMenuComponent) { - primaryMenu = React.createElement(route.primaryMenuComponent, compProps); - } - - if (route.secondaryMenuComponent) { - secondaryMenu = React.createElement(route.secondaryMenuComponent, compProps); - } - - const panelContent = ( -
    - {this.props.flashMessage} - {panel} -
    - ); - - if (panelInFullScreen) { - content = panelContent; - } else { - content = ( - <> -
    - - -
    - {panelContent} - - ); - } - - } else { - content = ( -
    - {t('loading')} -
    - ); - } - - if (panelInFullScreen) { - return ( -
    -
    -
    - {content} -
    -
    -
    - ); - - } else { - return ( -
    -
    - -
    - -
    - {showSidebar && -
    - {secondaryMenu} -
    - } -
    - {content} -
    -
    - - -
    - ); - } - } - } -} - - -@withRouter -@withComponentMixins([ - withErrorHandling -]) -export class SectionContent extends Component { - constructor(props) { - super(props); - - this.state = { - flashMessageText: '' - }; - - 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('/login?next=' + encodeURIComponent(window.location.pathname)); - } - } - - errorHandler(error) { - if (error instanceof interoperableErrors.NotLoggedInError) { - if (window.location.pathname !== '/login') { // There may be multiple async requests failing at the same time. So we take the pathname only from the first one. - this.navigateTo('/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 = styles.toolbar + ' ' + styles.buttonRow; - if (this.props.className) { - className += ' ' + this.props.className; - } - - return ( -
    - {this.props.children} -
    - ); - } -} - -export class LinkButton extends Component { - static propTypes = { - label: PropTypes.string, - icon: PropTypes.string, - className: PropTypes.string, - to: PropTypes.string - }; - - render() { - const props = this.props; - - return ( - + } + +
    Mailtrain
    + + + +
    + {primaryMenu} +
    + + + +
    + {showSidebar && +
    + {secondaryMenu} +
    + } +
    + {content} +
    +
    + + +
    + ); + } + } + } +} + + +@withRouter +@withComponentMixins([ + withErrorHandling +]) +export class SectionContent extends Component { + constructor(props) { + super(props); + + this.state = { + flashMessageText: '' + }; + + 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('/login?next=' + encodeURIComponent(window.location.pathname)); + } + } + + errorHandler(error) { + if (error instanceof interoperableErrors.NotLoggedInError) { + if (window.location.pathname !== '/login') { // There may be multiple async requests failing at the same time. So we take the pathname only from the first one. + this.navigateTo('/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 = styles.toolbar + ' ' + styles.buttonRow; + if (this.props.className) { + className += ' ' + this.props.className; + } + + return ( +
    + {this.props.children} +
    + ); + } +} + +export class LinkButton extends Component { + static propTypes = { + label: PropTypes.string, + icon: PropTypes.string, + className: PropTypes.string, + to: PropTypes.string + }; + + render() { + const props = this.props; + + return ( +