at the end of the day, it was inevitable
This commit is contained in:
+399
@@ -0,0 +1,399 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { translate } from 'react-i18next';
|
||||
import {
|
||||
timezones,
|
||||
getCurrentTimezone
|
||||
} from '../../../../../../common/Timezones';
|
||||
import Select from 'react-select';
|
||||
import moment from 'moment';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import RecipientsSelect from './RecipientsSelect';
|
||||
import CheckboxField from './CheckboxField';
|
||||
import RadioField from './RadioField';
|
||||
import BooleanRadioGroup from './BooleanRadioGroup';
|
||||
import SourcesDropTarget from './sources/SourcesDropTarget';
|
||||
import Sources from './sources/Sources';
|
||||
import Scheduling from './scheduling/Scheduling';
|
||||
import SaveAsPopup from './SaveAsPopup';
|
||||
import History from './History';
|
||||
import { EXTRAS } from '../../../../../../redux/modules/appState/share/forms/alertForm';
|
||||
import { THEME_TYPES } from '../../../../../../redux/modules/appState/share/forms/notificationForm';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardTitle,
|
||||
Col,
|
||||
Container,
|
||||
CustomInput,
|
||||
Form,
|
||||
FormGroup,
|
||||
Input,
|
||||
Label
|
||||
} from 'reactstrap';
|
||||
|
||||
export class AlertForm extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
state: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
switchShareSubScreen: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
changeName = (event) => {
|
||||
this.props.actions.changeName(event.target.value);
|
||||
};
|
||||
|
||||
changeSubject = (event) => {
|
||||
this.props.actions.changeSubject(event.target.value);
|
||||
};
|
||||
|
||||
changeAutoSubject = () => {
|
||||
const { state, actions } = this.props;
|
||||
actions.changeAutoSubject(!state.automatedSubject);
|
||||
};
|
||||
|
||||
changeRecipient = (value) => {
|
||||
this.props.actions.changeRecipients(value.split(','));
|
||||
};
|
||||
|
||||
changeTimezone = (zone) => {
|
||||
this.props.actions.changeTimezone(zone.value);
|
||||
};
|
||||
|
||||
toggleTimezone = () => {
|
||||
const { state, actions } = this.props;
|
||||
if (state.isEnabledTimezone) {
|
||||
actions.changeTimezone(getCurrentTimezone());
|
||||
}
|
||||
actions.toggleTimezone();
|
||||
};
|
||||
|
||||
changeSendUntil = (value) => {
|
||||
const sendUntil = value ? moment(value).format('YYYY-MM-DD') : '';
|
||||
this.props.actions.changeSendUntil(sendUntil);
|
||||
};
|
||||
|
||||
cancel = () => {
|
||||
this.props.switchShareSubScreen('notifications', 'tables');
|
||||
};
|
||||
|
||||
create = () => {
|
||||
const { state, actions } = this.props;
|
||||
const isEdit = !!state.id;
|
||||
actions.saveAlert(isEdit);
|
||||
};
|
||||
|
||||
edit = (name) => {
|
||||
const { actions } = this.props;
|
||||
actions.changeName(name);
|
||||
actions.saveAlert(false);
|
||||
};
|
||||
|
||||
showSaveAsPopup = () => {
|
||||
this.props.actions.toggleSaveAsPopup();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { state, actions, t } = this.props;
|
||||
const isEdit = !!state.id;
|
||||
const name = state.name;
|
||||
|
||||
const extract = state.content.extract;
|
||||
const userComments = state.content.showInfo.userComments;
|
||||
|
||||
const sendUntil = !state.sendUntil
|
||||
? state.sendUntil
|
||||
: moment(state.sendUntil).toDate();
|
||||
|
||||
return (
|
||||
<Card className="main-card mb-3">
|
||||
<CardBody>
|
||||
<CardTitle>
|
||||
{isEdit
|
||||
? t('notificationsTab.form.editAlert')
|
||||
: t('notificationsTab.form.createAlert')}
|
||||
</CardTitle>
|
||||
<div className="share-tab-form-container">
|
||||
<Container>
|
||||
<Form>
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.name')}</Label>
|
||||
<Col sm={10}>
|
||||
<Input
|
||||
type="text"
|
||||
title={t('notificationsTab.form.nameTooltip')}
|
||||
value={name}
|
||||
onChange={this.changeName}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<RecipientsSelect t={t} state={state} actions={actions} />
|
||||
|
||||
<CheckboxField
|
||||
label={t('notificationsTab.form.automatedEmail')}
|
||||
additionalLabel={t(
|
||||
'notificationsTab.form.automatedEmailDesc'
|
||||
)}
|
||||
value={state.automatedSubject}
|
||||
onChange={this.changeAutoSubject}
|
||||
/>
|
||||
|
||||
{!state.automatedSubject && (
|
||||
<FormGroup row>
|
||||
<Label sm={2}>
|
||||
{t('notificationsTab.form.emailSubject')}
|
||||
</Label>
|
||||
<Col sm={10}>
|
||||
<Input
|
||||
type="text"
|
||||
title={t('notificationsTab.form.emailSubject')}
|
||||
value={state.subject}
|
||||
onChange={this.changeSubject}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
)}
|
||||
|
||||
<CheckboxField
|
||||
label={t('notificationsTab.form.publish')}
|
||||
additionalLabel={t('notificationsTab.form.publishDesc')}
|
||||
value={state.published}
|
||||
onChange={actions.changePublished}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t('notificationsTab.form.unsubscribe')}
|
||||
additionalLabel={t('notificationsTab.form.unsubscribeDesc')}
|
||||
value={state.allowUnsubscribe}
|
||||
onChange={actions.changeAllowUnsubscribe}
|
||||
/>
|
||||
|
||||
<CheckboxField
|
||||
label={t('notificationsTab.form.notifications')}
|
||||
additionalLabel={t('notificationsTab.form.notificationsDesc')}
|
||||
value={state.unsubscribeNotification}
|
||||
onChange={actions.changeUnsubscribeNotification}
|
||||
/>
|
||||
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.feeds')}</Label>
|
||||
<Col sm={10}>
|
||||
<Sources
|
||||
sources={state.sources}
|
||||
removeSource={actions.removeSource}
|
||||
moveSource={actions.moveSource}
|
||||
/>
|
||||
<SourcesDropTarget addSource={actions.addSource} />
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.options')}</Label>
|
||||
<Col sm={10}>
|
||||
<FormGroup row>
|
||||
<Col sm={2}>
|
||||
{t('notificationsTab.form.articleExtracts')}
|
||||
</Col>
|
||||
<Col sm={10}>
|
||||
<div className="d-flex">
|
||||
<RadioField
|
||||
label={t(
|
||||
'notificationsTab.form.contextualExtracts'
|
||||
)}
|
||||
name="articleExtracts"
|
||||
checkedValue={extract}
|
||||
value={EXTRAS.CONTEXTUAL}
|
||||
onChange={actions.changeExtras}
|
||||
/>
|
||||
|
||||
<RadioField
|
||||
label={t('notificationsTab.form.startExtracts')}
|
||||
name="articleExtracts"
|
||||
checkedValue={extract}
|
||||
value={EXTRAS.START}
|
||||
onChange={actions.changeExtras}
|
||||
/>
|
||||
|
||||
<RadioField
|
||||
label={t('notificationsTab.form.noExtracts')}
|
||||
name="articleExtracts"
|
||||
checkedValue={extract}
|
||||
value={EXTRAS.NO}
|
||||
onChange={actions.changeExtras}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<BooleanRadioGroup
|
||||
mainLabel={t('notificationsTab.form.highlightKeywords')}
|
||||
name="highlightKeywords"
|
||||
value={state.content.highlightKeywords.highlight}
|
||||
onChange={actions.changeHighlightKeywords}
|
||||
/>
|
||||
|
||||
<BooleanRadioGroup
|
||||
mainLabel={t('notificationsTab.form.showSourceCountry')}
|
||||
name="showSourceCountry"
|
||||
value={state.content.showInfo.sourceCountry}
|
||||
onChange={actions.changeShowSourceCountry}
|
||||
/>
|
||||
|
||||
<FormGroup row>
|
||||
<Col sm={2}>
|
||||
{t('notificationsTab.form.showUserComments')}
|
||||
</Col>
|
||||
<Col sm={10}>
|
||||
<div className="d-flex">
|
||||
<RadioField
|
||||
label={t('common:commonWords.Yes')}
|
||||
name="showUserComments"
|
||||
checkedValue={userComments}
|
||||
value="with_author_date"
|
||||
onChange={actions.changeShowUserComments}
|
||||
/>
|
||||
|
||||
<RadioField
|
||||
label={t('common:commonWords.No')}
|
||||
name="showUserComments"
|
||||
checkedValue={userComments}
|
||||
value="no"
|
||||
onChange={actions.changeShowUserComments}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup row>
|
||||
<Col sm={2}>{t('notificationsTab.form.layout')}</Col>
|
||||
<Col sm={10}>
|
||||
<div className="d-flex">
|
||||
<RadioField
|
||||
label={t('notificationsTab.form.enhancedHtml')}
|
||||
name="themeType"
|
||||
checkedValue={state.themeType}
|
||||
value={THEME_TYPES.ENHANCED}
|
||||
onChange={actions.changeThemeType}
|
||||
/>
|
||||
|
||||
<RadioField
|
||||
label={t('notificationsTab.form.plainHtml')}
|
||||
name="themeType"
|
||||
checkedValue={state.themeType}
|
||||
value={THEME_TYPES.PLAIN}
|
||||
onChange={actions.changeThemeType}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<BooleanRadioGroup
|
||||
mainLabel={t('notificationsTab.form.sendWhenEmpty')}
|
||||
name="sendWhenEmpty"
|
||||
value={state.sendWhenEmpty}
|
||||
onChange={actions.changeSendWhenEmpty}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.timezone')}</Label>
|
||||
<Col sm={10}>
|
||||
<Select
|
||||
value={state.timezone}
|
||||
options={timezones}
|
||||
clearable={false}
|
||||
disabled={!state.isEnabledTimezone}
|
||||
onChange={this.changeTimezone}
|
||||
/>
|
||||
<CustomInput
|
||||
id="toggleTimezone"
|
||||
type="checkbox"
|
||||
className="mt-1"
|
||||
checked={state.isEnabledTimezone}
|
||||
onChange={this.toggleTimezone}
|
||||
label={t('notificationsTab.form.change')}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.automatic')}</Label>
|
||||
<Col sm={10}>
|
||||
<Scheduling state={state.scheduling} actions={actions} />
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.sendUntil')}</Label>
|
||||
<Col sm={4}>
|
||||
<DatePicker
|
||||
className="form-control"
|
||||
wrapperClassName="position-relative z-index-0"
|
||||
dateFormat="yyyy-MM-dd"
|
||||
placeholderText={t('notificationsTab.form.selectDate')}
|
||||
selected={sendUntil}
|
||||
minDate={moment()}
|
||||
onChange={this.changeSendUntil}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
||||
{isEdit && (
|
||||
<Fragment>
|
||||
<hr />
|
||||
<History
|
||||
notificationId={state.id}
|
||||
state={state.sendHistory}
|
||||
actions={actions}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<div className="text-right mb-3">
|
||||
<Button
|
||||
className="btn-icon"
|
||||
color="secondary"
|
||||
onClick={this.cancel}
|
||||
>
|
||||
<i className="lnr lnr-cross btn-icon-wrapper" />{' '}
|
||||
{t('notificationsTab.form.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
className="btn-icon ml-2"
|
||||
color="success"
|
||||
onClick={this.create}
|
||||
>
|
||||
<i className="lnr lnr-checkmark-circle btn-icon-wrapper" />
|
||||
{t('notificationsTab.form.save')}
|
||||
</Button>
|
||||
<Button
|
||||
className="btn-icon ml-2"
|
||||
color="success"
|
||||
onClick={this.showSaveAsPopup}
|
||||
>
|
||||
<i className="lnr lnr-checkmark-circle btn-icon-wrapper" />
|
||||
{t('notificationsTab.form.saveAs')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Container>
|
||||
|
||||
{state.showSaveAsPopup && (
|
||||
<SaveAsPopup
|
||||
name={name}
|
||||
togglePopup={actions.toggleSaveAsPopup}
|
||||
onSubmit={this.edit}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(AlertForm);
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import RadioField from './RadioField'
|
||||
import { Col, FormGroup } from 'reactstrap'
|
||||
import { translate } from 'react-i18next'
|
||||
|
||||
export class BooleanRadioGroup extends React.PureComponent {
|
||||
static propTypes = {
|
||||
mainLabel: PropTypes.string.isRequired,
|
||||
trueLabel: PropTypes.string,
|
||||
falseLabel: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
onChange = (event) => {
|
||||
const { onChange } = this.props
|
||||
let value = event.target.value
|
||||
if (value === 'true' || value === 'false') {
|
||||
value = value === 'true'
|
||||
}
|
||||
|
||||
onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
trueLabel = this.props.t('commonWords.Yes'),
|
||||
falseLabel = this.props.t('commonWords.No'),
|
||||
mainLabel,
|
||||
name,
|
||||
value,
|
||||
onChange
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<FormGroup row>
|
||||
<Col sm={2}>{mainLabel}</Col>
|
||||
<Col sm={10}>
|
||||
<div className="d-flex">
|
||||
<RadioField
|
||||
label={trueLabel}
|
||||
name={name}
|
||||
checkedValue={value}
|
||||
value
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
||||
<RadioField
|
||||
label={falseLabel}
|
||||
name={name}
|
||||
checkedValue={value}
|
||||
value={false}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(['common'], { wait: true })(BooleanRadioGroup)
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Col, CustomInput, FormGroup, Label } from 'reactstrap'
|
||||
|
||||
export class CheckboxField extends React.PureComponent {
|
||||
static propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
additionalLabel: PropTypes.string.isRequired,
|
||||
value: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
onChange = () => {
|
||||
const { onChange, value } = this.props
|
||||
onChange(!value)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { label, additionalLabel, value } = this.props
|
||||
|
||||
return (
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{label}</Label>
|
||||
<Col sm={10}>
|
||||
<CustomInput
|
||||
id={label}
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
onChange={this.onChange}
|
||||
label={additionalLabel}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CheckboxField
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { Button, Collapse, ListGroup, ListGroupItem } from 'reactstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import { translate } from 'react-i18next'
|
||||
import { convertUTCtoLocal } from '../../../../../../common/helper'
|
||||
|
||||
export class History extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
notificationId: PropTypes.number.isRequired,
|
||||
state: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
onToggle = () => {
|
||||
const { state, actions } = this.props
|
||||
if (!state.isOpen && !state.isLoadingCompleted) {
|
||||
this.showMore()
|
||||
}
|
||||
actions.toggleHistory()
|
||||
}
|
||||
|
||||
showMore = () => {
|
||||
const { notificationId, state, actions } = this.props
|
||||
actions.getHistory(notificationId, state.page + 1, state.limit)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { state, t } = this.props
|
||||
const isOpen = state.isOpen
|
||||
const label = isOpen ? t('notificationsTab.history.hideSendHistory') : t('notificationsTab.history.showSendHistory')
|
||||
const iconClasses = classnames('lnr mr-2', {
|
||||
'lnr-chevron-right': !isOpen,
|
||||
'lnr-chevron-down': isOpen
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="history">
|
||||
<Button
|
||||
color="link"
|
||||
className="p-0 font-size-md"
|
||||
onClick={this.onToggle}
|
||||
>
|
||||
<i className={iconClasses} />
|
||||
{label}
|
||||
</Button>
|
||||
<Collapse isOpen={isOpen}>
|
||||
{state.isLoadingCompleted && (
|
||||
<div className="mt-3 ml-4">
|
||||
<p className="text-muted mb-1">{t('notificationsTab.history.sentTime')}</p>
|
||||
<ListGroup>
|
||||
{state.entities.map((entity, i) => (
|
||||
<ListGroupItem
|
||||
className="col-sm-6 p-2"
|
||||
key={`history-date-${i}`}
|
||||
>
|
||||
{convertUTCtoLocal(entity.date, 'DD MMM YYYY HH:mm')}
|
||||
</ListGroupItem>
|
||||
))}
|
||||
</ListGroup>
|
||||
{!state.isPending && state.entities.length < state.totalCount && (
|
||||
<Button color="link" onClick={this.showMore}>
|
||||
{t('notificationsTab.history.showMore')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{state.isPending && (
|
||||
<p className="ml-4 mt-3">
|
||||
<FontAwesomeIcon icon={faSpinner} className="mr-2" pulse /> {t('notificationsTab.history.loading')}
|
||||
</p>
|
||||
)}
|
||||
</Collapse>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(History)
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import { Card, CardBody, CardTitle, Col, Form, FormGroup, Input, Label } from 'reactstrap'
|
||||
|
||||
export class NewsletterForm extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
state: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
changeName = (event) => {
|
||||
this.props.actions.changeName(event.target.value)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { state, t } = this.props
|
||||
|
||||
return (
|
||||
<Card className="main-card mb-3">
|
||||
<CardBody>
|
||||
<CardTitle>{t('notificationsTab.newsLetter.createNewsletter')}</CardTitle>
|
||||
<Form>
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.newsLetter.name')}</Label>
|
||||
<Col sm={10}>
|
||||
<Input type="text" value={state.name} onChange={this.changeName} />
|
||||
</Col>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
export default translate(['tabsContent'], { wait: true })(NewsletterForm)
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { CustomInput } from 'reactstrap'
|
||||
|
||||
export class RadioField extends React.PureComponent {
|
||||
static propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
checkedValue: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool
|
||||
]),
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool
|
||||
]),
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
onChange = (event) => {
|
||||
const { onChange } = this.props
|
||||
let value = event.target.value
|
||||
if (value === 'true' || value === 'false') {
|
||||
value = value === 'true'
|
||||
}
|
||||
|
||||
onChange(value)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { label, name, checkedValue, value } = this.props
|
||||
|
||||
return (
|
||||
<CustomInput
|
||||
id={`${name}_${value}`}
|
||||
type="radio"
|
||||
className="mr-2"
|
||||
name={name}
|
||||
value={value}
|
||||
checked={checkedValue === value}
|
||||
onChange={this.onChange}
|
||||
label={label}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RadioField
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Select from 'react-select'
|
||||
import { Col, FormGroup, Label } from 'reactstrap'
|
||||
|
||||
export class RecipientsSelect extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
state: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
loadOptions = (input) => {
|
||||
const { actions } = this.props
|
||||
return actions.getRecipients(input)
|
||||
};
|
||||
|
||||
changeRecipient = (value) => {
|
||||
const { actions } = this.props
|
||||
actions.changeRecipients(value)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { state, t } = this.props
|
||||
const recipients = state.recipients
|
||||
|
||||
return (
|
||||
<FormGroup row>
|
||||
<Label sm={2}>{t('notificationsTab.form.recipient')}</Label>
|
||||
<Col sm={10}>
|
||||
<Select.Async
|
||||
name="recipient-select"
|
||||
loadOptions={this.loadOptions}
|
||||
multi
|
||||
value={recipients}
|
||||
onChange={this.changeRecipient}
|
||||
/>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RecipientsSelect
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Label,
|
||||
FormGroup,
|
||||
Input
|
||||
} from 'reactstrap'
|
||||
|
||||
export class SaveAsPopup extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
togglePopup: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
t: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
name: `${props.name} (copy)`
|
||||
}
|
||||
}
|
||||
|
||||
hidePopup = () => {
|
||||
this.props.togglePopup()
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
this.props.onSubmit(this.state.name)
|
||||
this.hidePopup()
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
const { name, value } = e.target
|
||||
this.setState({ [name]: value })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t } = this.props
|
||||
|
||||
return (
|
||||
<Modal isOpen toggle={this.hidePopup} backdrop="static">
|
||||
<ModalHeader toggle={this.hidePopup}>
|
||||
{t('notificationsTab.popup.saveAs')}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<FormGroup>
|
||||
<Label>{t('notificationsTab.popup.saveAsPlaceholder')}</Label>
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
value={this.state.name}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="light" onClick={this.hidePopup}>
|
||||
{t('common:commonWords.Cancel')}
|
||||
</Button>
|
||||
<Button color="primary" onClick={this.onSubmit}>
|
||||
{t('notificationsTab.popup.save')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(['tabsContent', 'common'], { wait: true })(SaveAsPopup)
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import ScheduleSelectField from './ScheduleSelectField'
|
||||
import { IoIosCloseCircleOutline } from 'react-icons/io'
|
||||
|
||||
export class ScheduleOptions extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
id: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
]),
|
||||
type: PropTypes.string.isRequired,
|
||||
item: PropTypes.object.isRequired,
|
||||
constants: PropTypes.object.isRequired,
|
||||
canDelete: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onRemove: PropTypes.func
|
||||
};
|
||||
|
||||
onChange = (field, value) => {
|
||||
const { id, item, onChange } = this.props
|
||||
const newItem = {
|
||||
...item,
|
||||
[field]: value
|
||||
}
|
||||
onChange(id, newItem)
|
||||
};
|
||||
|
||||
onRemove = () => {
|
||||
const { id, canDelete, onRemove } = this.props
|
||||
if (canDelete && onRemove) {
|
||||
onRemove(id)
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { t, id, type, item, canDelete = false, constants } = this.props
|
||||
const showTime = (type === 'daily' && item.time === 'once') || (type !== 'daily')
|
||||
|
||||
return (
|
||||
<div className="schedule-options">
|
||||
{id !== 'new' &&
|
||||
<div>{t(`notificationsTab.form.type.${type}`)} </div>
|
||||
}
|
||||
<p>Send</p>
|
||||
{type === 'daily' &&
|
||||
<div className="schedule-options__group">
|
||||
<ScheduleSelectField
|
||||
field='time'
|
||||
items={constants.time}
|
||||
value={item.time}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
|
||||
<ScheduleSelectField
|
||||
field='days'
|
||||
items={constants.days}
|
||||
value={item.days}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{type === 'weekly' &&
|
||||
<div className="schedule-options__group">
|
||||
<ScheduleSelectField
|
||||
field='period'
|
||||
items={constants.period}
|
||||
value={item.period}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
|
||||
<ScheduleSelectField
|
||||
field='day'
|
||||
items={constants.day}
|
||||
value={item.day}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{type === 'monthly' &&
|
||||
<div className="schedule-options__group">
|
||||
<ScheduleSelectField
|
||||
needTranslate={false}
|
||||
field='monthDay'
|
||||
items={constants.monthDay}
|
||||
value={item.monthDay}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{(type === 'weekly' || type === 'monthly') &&
|
||||
<div>of the month </div>
|
||||
}
|
||||
|
||||
{showTime &&
|
||||
<div className="schedule-options__group">
|
||||
<span>at</span>
|
||||
<ScheduleSelectField
|
||||
needTranslate={false}
|
||||
field='hour'
|
||||
items={constants.hour}
|
||||
value={item.hour}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
<span>:</span>
|
||||
<ScheduleSelectField
|
||||
needTranslate={false}
|
||||
field='minute'
|
||||
items={constants.minute}
|
||||
value={item.minute}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{canDelete && (
|
||||
<button
|
||||
title="Remove"
|
||||
type="button"
|
||||
className="btn p-0"
|
||||
onClick={this.onRemove}
|
||||
>
|
||||
<IoIosCloseCircleOutline size={22} className="text-danger ml-2" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(ScheduleOptions)
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import Select from 'react-select'
|
||||
import classnames from 'classnames'
|
||||
import { padLeft, addOrdinalSuffix } from '../../../../../../../common/StringUtils'
|
||||
|
||||
export class ScheduleSelectField extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
needTranslate: PropTypes.bool,
|
||||
field: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
]),
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
paddingFields = ['hour', 'minute'];
|
||||
suffixFields = ['monthDay'];
|
||||
|
||||
onChange = (item) => {
|
||||
const { field, onChange } = this.props
|
||||
onChange(field, item.value)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { t, needTranslate = true, items, value, field } = this.props
|
||||
const classes = classnames('schedule-select-field', `schedule-select-field--${field}`)
|
||||
const options = items.map(item => {
|
||||
let label = ''
|
||||
if (needTranslate) {
|
||||
label = t(`notificationsTab.form.${field}.${item}`)
|
||||
}
|
||||
else {
|
||||
label = (this.paddingFields.includes(field)) ? padLeft(item.toString(), 2) : item
|
||||
label = (this.suffixFields.includes(field)) ? addOrdinalSuffix(item) : label
|
||||
}
|
||||
return {
|
||||
value: item,
|
||||
label
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Select
|
||||
className={classes}
|
||||
options={options}
|
||||
value={value}
|
||||
clearable={false}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(ScheduleSelectField)
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import TypeSelector from './TypeSelector'
|
||||
import ScheduleOptions from './ScheduleOptions'
|
||||
import { Button, ListGroup, ListGroupItem } from 'reactstrap'
|
||||
|
||||
export class Scheduling extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
state: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
onChange = (id, item) => {
|
||||
const { actions } = this.props
|
||||
if (id === 'new') {
|
||||
actions.changeNewSchedule(item)
|
||||
} else {
|
||||
actions.changeExistingSchedule(item, id)
|
||||
}
|
||||
};
|
||||
|
||||
addSchedule = () => {
|
||||
this.props.actions.addSchedule()
|
||||
};
|
||||
|
||||
removeSchedule = (id) => {
|
||||
this.props.actions.removeSchedule(id)
|
||||
};
|
||||
|
||||
render () {
|
||||
const { state, actions, t } = this.props
|
||||
|
||||
const constants = state.constants
|
||||
const activeType = state.newTime.type
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TypeSelector
|
||||
types={constants.type}
|
||||
activeType={activeType}
|
||||
onChange={actions.changeScheduleType}
|
||||
/>
|
||||
|
||||
<div className="new-schedule mb-3">
|
||||
<ScheduleOptions
|
||||
id="new"
|
||||
type={activeType}
|
||||
item={state.newTime}
|
||||
constants={constants}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={this.addSchedule}
|
||||
>
|
||||
{t('notificationsTab.form.add')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="schedule-list">
|
||||
<p className="text-muted mb-1">
|
||||
{t('notificationsTab.form.activeScheduledTimes')} <span>({state.times.length})</span>
|
||||
</p>
|
||||
|
||||
<ListGroup>
|
||||
{state.times.map((time, i) => {
|
||||
return (
|
||||
<ListGroupItem
|
||||
key={'schedule--added-time-' + i}
|
||||
>
|
||||
<ScheduleOptions
|
||||
id={i}
|
||||
type={time.type}
|
||||
item={time}
|
||||
canDelete
|
||||
constants={constants}
|
||||
onChange={this.onChange}
|
||||
onRemove={this.removeSchedule}
|
||||
/>
|
||||
</ListGroupItem>
|
||||
)
|
||||
})}
|
||||
</ListGroup>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(Scheduling)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { translate } from 'react-i18next'
|
||||
import RadioField from '../RadioField'
|
||||
|
||||
export class TypeSelector extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
types: PropTypes.array.isRequired,
|
||||
activeType: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { t, types, activeType, onChange } = this.props
|
||||
|
||||
return (
|
||||
<div className="d-flex mb-2">
|
||||
{types.map((type, i) => {
|
||||
return (
|
||||
<RadioField
|
||||
key={'schedule-type-' + i}
|
||||
label={t(`notificationsTab.form.type.${type}`)}
|
||||
name="schedule-type"
|
||||
checkedValue={activeType}
|
||||
value={type}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default translate(['tabsContent'], { wait: true })(TypeSelector)
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {
|
||||
IoIosArrowDropup,
|
||||
IoIosArrowDropdown,
|
||||
IoIosCloseCircleOutline
|
||||
} from 'react-icons/io'
|
||||
|
||||
export class Source extends React.Component {
|
||||
static propTypes = {
|
||||
source: PropTypes.object.isRequired,
|
||||
removeSource: PropTypes.func.isRequired,
|
||||
moveSource: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
onRemove = () => {
|
||||
const { source, removeSource } = this.props
|
||||
removeSource(source.id)
|
||||
}
|
||||
|
||||
onMoveUp = () => {
|
||||
const { source, moveSource } = this.props
|
||||
moveSource(source.id, true)
|
||||
}
|
||||
|
||||
onMoveDown = () => {
|
||||
const { source, moveSource } = this.props
|
||||
moveSource(source.id, false)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { source } = this.props
|
||||
|
||||
return (
|
||||
<div className="d-flex mr-2 mb-2">
|
||||
<p
|
||||
className={classnames(
|
||||
'd-flex align-items-center feed-icon',
|
||||
source.class
|
||||
)}
|
||||
>
|
||||
{source.name}
|
||||
</p>
|
||||
<div className="ml-sm-4">
|
||||
<button
|
||||
title="Up"
|
||||
type="button"
|
||||
className="btn p-0"
|
||||
onClick={this.onMoveUp}
|
||||
>
|
||||
<IoIosArrowDropup size={22} className="text-secondary ml-2" />
|
||||
</button>
|
||||
<button
|
||||
title="Down"
|
||||
type="button"
|
||||
className="btn p-0"
|
||||
onClick={this.onMoveDown}
|
||||
>
|
||||
<IoIosArrowDropdown size={22} className="text-secondary ml-2" />
|
||||
</button>
|
||||
<button
|
||||
title="Remove"
|
||||
type="button"
|
||||
className="btn p-0"
|
||||
onClick={this.onRemove}
|
||||
>
|
||||
<IoIosCloseCircleOutline size={22} className="text-danger ml-2" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Source
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Source from './Source'
|
||||
|
||||
export class Sources extends React.Component {
|
||||
static propTypes = {
|
||||
sources: PropTypes.array.isRequired,
|
||||
removeSource: PropTypes.func.isRequired,
|
||||
moveSource: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { sources, removeSource, moveSource } = this.props
|
||||
|
||||
return (
|
||||
<div>
|
||||
{sources.map((source, i) => {
|
||||
return (
|
||||
<Source
|
||||
key={'dragged-source-item-' + i}
|
||||
source={source}
|
||||
removeSource={removeSource}
|
||||
moveSource={moveSource}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Sources
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { compose } from 'redux'
|
||||
import { DropTarget } from 'react-dnd'
|
||||
import { translate } from 'react-i18next'
|
||||
|
||||
const target = {
|
||||
drop (props, monitor) {
|
||||
const item = monitor.getItem()
|
||||
props.addSource(item.feed)
|
||||
},
|
||||
|
||||
canDrop (props, monitor) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export class SourcesDropTargetClass extends React.Component {
|
||||
static propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
connectDropTarget: PropTypes.func.isRequired,
|
||||
addSource: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
const { connectDropTarget, t } = this.props
|
||||
|
||||
return connectDropTarget(
|
||||
<div className='dropzone-wrapper dropzone-wrapper-sm'>
|
||||
<p className="dropzone-content">{t('notificationsTab.form.dragFeed')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const SourcesDropTarget = compose(
|
||||
DropTarget('feed', target, (connect, monitor) => ({
|
||||
connectDropTarget: connect.dropTarget(),
|
||||
itemType: monitor.getItemType()
|
||||
})),
|
||||
translate(['tabsContent'], { wait: true })
|
||||
)(SourcesDropTargetClass)
|
||||
|
||||
export default SourcesDropTarget
|
||||
Reference in New Issue
Block a user