at the end of the day, it was inevitable

This commit is contained in:
Mo Elzubeir
2022-12-09 08:36:26 -06:00
commit 1218570914
1768 changed files with 887087 additions and 0 deletions
@@ -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);
@@ -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)
@@ -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)
@@ -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)
@@ -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
@@ -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
@@ -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)
@@ -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}`)}&nbsp;</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&nbsp;</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)
@@ -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)
@@ -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)
@@ -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)
@@ -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
@@ -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
@@ -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