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,60 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import ExportFeedsTableRow from './ExportFeedsTableRow'
import LoadersAdvanced from '../../../../common/Loader/Loader'
import { Table, Card, CardBody } from 'reactstrap'
class ExportFeedsTable extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
isLoading: PropTypes.bool.isRequired,
tableData: PropTypes.array.isRequired,
showPopup: PropTypes.func.isRequired,
unexportFeed: PropTypes.func.isRequired,
goToFeed: PropTypes.func.isRequired
}
render() {
const {
tableData,
isLoading,
showPopup,
unexportFeed,
goToFeed,
t
} = this.props
return (
<Card className="main-card mb-3">
{isLoading && <LoadersAdvanced />}
<CardBody>
<Table striped bordered className="mb-0">
<thead>
<tr>
<th>{t('exportTab.feedName')}</th>
<th>{t('exportTab.exportWith')}</th>
<th>{t('exportTab.actions')}</th>
</tr>
</thead>
<tbody>
{tableData.map((feed) => {
return (
<ExportFeedsTableRow
key={feed.id}
feed={feed}
showPopup={showPopup}
unexportFeed={unexportFeed}
goToFeed={goToFeed}
/>
)
})}
</tbody>
</Table>
</CardBody>
</Card>
)
}
}
export default translate(['tabsContent'], { wait: true })(ExportFeedsTable)
@@ -0,0 +1,132 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Interpolate, translate } from 'react-i18next';
import Select from 'react-select';
import { Modal, ModalBody, ModalFooter, Button, ModalHeader } from 'reactstrap';
class ExportFeedsTableRow extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
feed: PropTypes.object.isRequired,
showPopup: PropTypes.func.isRequired,
unexportFeed: PropTypes.func.isRequired,
goToFeed: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
format: 'rss',
modal: false
};
}
showExportPopup = () => {
this.props.showPopup(this.props.feed, this.state.format);
};
toggle = () => {
this.setState((prev) => ({ modal: !prev.modal }));
};
exportOptions = [
{ label: 'RSS 2.0', value: 'rss' },
{ label: 'Atom 1.0', value: 'atom' },
{ label: 'TSV', value: 'tsv' },
{ label: 'HTML', value: 'html' }
];
onChangeFormat = (format) => {
this.setState({
format: format
});
};
onDeleteClick = () => {
this.setState({ modal: false });
this.props.unexportFeed(this.props.feed.id);
};
goToFeed = (e) => {
e.preventDefault();
this.props.goToFeed(this.props.feed.id);
};
render() {
const { feed, t } = this.props;
return (
<tr>
<td>
<Button
color="link"
className={`feed-icon font-size-lg p-0 feed-type-mixed ${feed.class}`}
onClick={this.goToFeed}
>
{feed.name}
</Button>
</td>
<td>
<Select
options={this.exportOptions}
value={this.state.format}
simpleValue
onChange={this.onChangeFormat}
clearable={false}
/>
</td>
<td>
<Button
size="sm"
color="primary"
className="border-0 mr-2"
onClick={this.showExportPopup}
>
{t('exportTab.export')}
</Button>
<Button
outline
size="sm"
color="secondary"
className="border-0"
onClick={this.toggle}
>
{t('exportTab.delete')}
</Button>
<Modal
isOpen={this.state.modal}
toggle={this.toggle}
backdrop="static"
>
<ModalHeader toggle={this.toggle}>
{t('exportTab.confirm')}
</ModalHeader>
<ModalBody>
<p>
<Interpolate
t={t}
i18nKey="exportTab.exportDeleteMessage"
feedName={feed.name}
/>
</p>
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.toggle}>
{t('common:commonWords.Cancel')}
</Button>
<Button color="danger" onClick={this.onDeleteClick}>
{t('common:commonWords.Delete')}
</Button>
</ModalFooter>
</Modal>
</td>
</tr>
);
}
}
export default translate(['tabsContent'], { wait: true })(ExportFeedsTableRow);
@@ -0,0 +1,98 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import config from '../../../../../appConfig';
import {
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Table
} from 'reactstrap';
class ExportPopup extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
feed: PropTypes.object.isRequired,
hidePopup: PropTypes.func.isRequired,
exportFormat: PropTypes.string.isRequired
};
hidePopup = () => {
this.props.hidePopup();
};
hidePopupFromOutside = (e) => {
if (e.target === e.currentTarget) this.hidePopup();
};
exportOptions = {
rss: 'RSS 2.0',
atom: 'Atom 1.0',
tsv: 'TSV',
html: 'HTML'
};
render() {
const { t, feed, exportFormat } = this.props;
const href = `${config.apiUrl}/feed/${feed.id}.${exportFormat}`;
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static" size="lg">
<ModalHeader toggle={this.hidePopup}>
{this.exportOptions[exportFormat] + ' ' + t('exportTab.export')}
</ModalHeader>
<ModalBody>
<div className="mb-4">
<p>{t('exportTab.exportPopup.line1')}</p>
<p className="text-muted font-size-xs mb-2">
({t('exportTab.exportPopup.line2')})
</p>
<a
href={href}
target="_blank"
className="font-weight-bold"
rel="noopener noreferrer"
>
{href}
</a>
</div>
<p className="mb-2">{t('exportTab.exportPopup.line3')}</p>
<Table striped>
<tbody>
<tr>
<th scope="row">n</th>
<td>{t('exportTab.exportPopup.param1')}</td>
</tr>
<tr>
<th scope="row">ext</th>
<td>{t('exportTab.exportPopup.param2')}</td>
</tr>
{exportFormat !== 'tsv' && (
<tr>
<th scope="row">img</th>
<td>{t('exportTab.exportPopup.param3')}</td>
</tr>
)}
{exportFormat !== 'tsv' && exportFormat !== 'html' && (
<tr>
<th scope="row">text_format</th>
<td>{t('exportTab.exportPopup.param4')}</td>
</tr>
)}
</tbody>
</Table>
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('exportTab.close')}
</Button>
</ModalFooter>
</Modal>
);
}
}
export default translate(['tabsContent'], { wait: true })(ExportPopup);
@@ -0,0 +1,71 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import ExportFeedsTable from './ExportFeedsTable'
import ExportPopup from './ExportPopup'
import { withRouter } from 'react-router-dom'
import reduxConnect from '../../../../../redux/utils/connect'
import { compose } from 'redux'
import { setDocumentData } from '../../../../../common/helper'
class ExportSubTab extends React.Component {
static propTypes = {
exportFeedsState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
componentDidMount() {
setDocumentData('title', 'Export | Share')
}
componentWillUnmount() {
setDocumentData('title')
}
componentWillMount = () => {
this.props.actions.loadExportedFeeds()
};
goToFeed = (feedId) => {
const {
history,
actions: { getFeedResults }
} = this.props
history.push('/app/search/search')
getFeedResults({ page: 1 }, feedId)
};
render () {
const { t, exportFeedsState, actions } = this.props
return (
<div>
<p className="text-muted mb-3">{t('exportTab.topMessage')}</p>
<ExportFeedsTable
isLoading={exportFeedsState.isLoading}
tableData={exportFeedsState.tableData}
showPopup={actions.showExportPopup}
unexportFeed={actions.unexportFeed}
goToFeed={this.goToFeed}
/>
{exportFeedsState.popupVisible && (
<ExportPopup
feed={exportFeedsState.selectedFeed}
hidePopup={actions.hideExportPopup}
exportFormat={exportFeedsState.exportFormat}
/>
)}
</div>
)
}
}
const applyDecorators = compose(
withRouter,
reduxConnect('exportFeedsState', ['appState', 'share', 'exportFeeds']),
translate(['tabsContent'], { wait: true })
)
export default applyDecorators(ExportSubTab)
@@ -0,0 +1,16 @@
import PropTypes from 'prop-types'
import {AlertForm as BaseAlertForm} from '../NotificatoinsSubTab/forms/AlertForm'
import {translate} from 'react-i18next'
export class AlertForm extends BaseAlertForm {
static propTypes = {
t: PropTypes.func.isRequired,
state: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
switchShareSubScreen: PropTypes.func.isRequired
}
}
export default translate(['tabsContent'], { wait: true })(AlertForm)
@@ -0,0 +1,91 @@
import React from 'react'
import { translate } from 'react-i18next'
import PropTypes from 'prop-types'
import SortableTh from '../../../../common/Table/SortableTh'
import { MyEmailsTable } from '../NotificatoinsSubTab/MyEmailsTable' // default export doesn't work
import { ButtonGroup, Button } from 'reactstrap'
class EmailsTable extends MyEmailsTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
nameClickAction = (item) => {
const { actions } = this.props
actions.startEditNotification(item, 'emails', 'emails')
};
defineColumns () {
return {
...super.defineColumns(),
owner: {
Header: <SortableTh title="manageEmailsTab.owner" />,
accessor: (item) => item.owner.email,
width: 170
}
}
}
onRefreshButtonClick = () => {
this.props.tableActions.loadTable({})
};
getColumns () {
return [
'selectCheckbox',
'name',
'type',
'owner',
'published',
'ScheduledTimes',
'sourcesCount',
'Recipients',
'active',
'delete'
]
}
getActionsPanel = () => {
const { t } = this.props
return (
<ButtonGroup className="mb-3">
<Button
onClick={this.onActivateButtonClick}
color="secondary"
>
<i className="fa fa-play fa-1px for-small mr-1"> </i>{" "}
{t('notificationsTab.activate')}
</Button>
<Button
color="secondary"
onClick={this.onPauseButtonClick}
>
<i className="fa fa-pause fa-1px for-small mr-1"> </i>{" "}
{t('notificationsTab.pause')}
</Button>
<Button
color="secondary"
onClick={this.onDeleteButtonClick}
>
<i className="fa fa-trash for-small mr-1"> </i>{" "}
{t('notificationsTab.delete')}
</Button>
<Button
color="secondary"
onClick={this.onRefreshButtonClick}
>
<i className="fa fa-refresh fa-1px for-small mr-1"> </i>{" "}
{t('manageEmailsTab.refresh')}
</Button>
</ButtonGroup>
)
};
}
export default translate(['tabsContent'], { wait: true })(EmailsTable)
@@ -0,0 +1,49 @@
import React from 'react'
import PropTypes from 'prop-types'
import {translate} from 'react-i18next'
import {GenericTable} from '../common/GenericTable'
import SortableTh from '../../../../common/Table/SortableTh'
import {EMAILS_SUBSCREENS} from '../../../../../redux/modules/appState/share/tabs'
export class FiltersTable extends GenericTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired
};
defineColumns () {
return {
'name': {
Header: <SortableTh title='manageEmailsTab.filter' />,
accessor: 'name'
},
'notifications': {
Header: <SortableTh title='manageEmailsTab.notifications' />,
width: 270,
accessor: 'notifications'
}
}
}
getColumns () {
return ['name', 'notifications']
}
onRowClick = (e, state, rowInfo) => {
const { actions } = this.props
const filter = {
type: rowInfo.original.type,
id: rowInfo.original.id,
name: rowInfo.original.name
}
actions.shareTables.emails.setFilter(filter)
actions.switchShareSubScreen('emails', EMAILS_SUBSCREENS.EMAILS_TABLE)
actions.shareTables.emails.loadTable({})
};
}
export default translate(['tabsContent'], { wait: true })(FiltersTable)
@@ -0,0 +1,74 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import Select from 'react-select'
import { EMAILS_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs'
import { Button } from 'reactstrap'
export class FiltersTopBar extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired,
filterType: PropTypes.string.isRequired,
t: PropTypes.func.isRequired
};
onSelectFilterType = (filterType) => {
const { actions } = this.props
actions.shareTables.emailFilters.loadTable({ filterType })
};
clearFilters = () => {
const { actions } = this.props
actions.shareTables.emails.clearFilter()
actions.switchShareSubScreen('emails', EMAILS_SUBSCREENS.EMAILS_TABLE)
};
backToTable = () => {
const { actions } = this.props
actions.switchShareSubScreen('emails', EMAILS_SUBSCREENS.EMAILS_TABLE)
}
filterTypes = [
{ label: 'Owner', value: 'owner' },
{ label: 'Recipient', value: 'recipient' },
{ label: 'Feed', value: 'feed' }
];
render () {
const { t, filterType } = this.props
return (
<Fragment>
<Button className="btn-wide mb-2" size="sm" color="info" onClick={this.backToTable}>
<i className="lnr lnr-chevron-left"> </i>
</Button>
<div className="notifications-topbar align-items-center">
<div className="text-muted">{t('manageEmailsTab.emailFilter')}</div>
<div className="d-flex align-items-center">
<label className="mr-1">{t('manageEmailsTab.filterBy')}</label>
<div style={{ minWidth: '150px' }}>
<Select
value={filterType}
onChange={this.onSelectFilterType}
options={this.filterTypes}
simpleValue
searchable={false}
clearable={false}
/>
</div>
<Button
color="secondary"
className="ml-2"
onClick={this.clearFilters}
>
{t('manageEmailsTab.allEmails')}
</Button>
</div>
</div>
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(FiltersTopBar)
@@ -0,0 +1,87 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import TopBar from './TopBar';
import EmailsTable from './EmailsTable';
import reduxConnect from '../../../../../redux/utils/connect';
import AlertForm from './AlertForm';
import Navigation from './Navigation';
import FiltersTable from './FiltersTable';
import FiltersTopBar from './FiltersTopBar';
import { EMAILS_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs';
import { setDocumentData } from '../../../../../common/helper';
class ManageEmailsSubTab extends React.Component {
static propTypes = {
shareState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
componentDidMount() {
setDocumentData('title', 'Manage Recipients | Share')
}
componentWillUnmount() {
setDocumentData('title')
}
render() {
const { shareState, actions } = this.props;
const { subScreenVisible } = shareState.tabs.emails;
return (
<div className="notifications-tab">
{subScreenVisible === EMAILS_SUBSCREENS.EMAILS_TABLE && (
<div>
<TopBar tableState={shareState.tables.emails} actions={actions} />
<EmailsTable
tableState={shareState.tables.emails}
actions={actions}
tableActions={actions.shareTables.emails}
deleteSingleText="email"
deleteMultipleText="emails"
/>
</div>
)}
{(subScreenVisible === EMAILS_SUBSCREENS.ALERT_FORM ||
subScreenVisible === EMAILS_SUBSCREENS.NEWSLETTER_FORM) && (
<Navigation actions={actions} />
)}
{subScreenVisible === EMAILS_SUBSCREENS.ALERT_FORM && (
<AlertForm
state={shareState.forms.alert}
switchShareSubScreen={actions.switchShareSubScreen}
actions={actions.shareForms.alert}
/>
)}
{/* {subScreenVisible === EMAILS_SUBSCREENS.NEWSLETTER_FORM &&
<NewsletterForm
state={shareState.forms.newsletter}
switchShareSubScreen={actions.switchShareSubScreen}
actions={actions.shareForms.newsletter}
/>
} */}
{subScreenVisible === EMAILS_SUBSCREENS.FILTERS_TABLE && (
<Fragment>
<FiltersTopBar
actions={actions}
filterType={shareState.tables.emailFilters.filterType}
/>
<FiltersTable
actions={actions}
tableState={shareState.tables.emailFilters}
tableActions={actions.shareTables.emailFilters}
/>
</Fragment>
)}
</div>
);
}
}
export default reduxConnect('shareState', ['appState', 'share'])(
ManageEmailsSubTab
);
@@ -0,0 +1,30 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { EMAILS_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs'
import { Button } from 'reactstrap'
class Navigation extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired
};
backToTable = () => {
this.props.actions.switchShareSubScreen(
'emails',
EMAILS_SUBSCREENS.EMAILS_TABLE
)
};
render () {
return (
<Button className="btn-wide mb-2" size="sm" color="info" onClick={this.backToTable}>
<i className="lnr lnr-chevron-left"> </i>
</Button>
)
}
}
export default translate(['tabsContent'], { wait: true })(Navigation)
@@ -0,0 +1,14 @@
import PropTypes from 'prop-types'
import {NewsletterForm as BaseNewsletterForm} from '../NotificatoinsSubTab/forms/NewsletterForm'
import {translate} from 'react-i18next'
export class NewsletterForm extends BaseNewsletterForm {
static propTypes = {
t: PropTypes.func.isRequired,
state: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
}
export default translate(['tabsContent'], { wait: true })(NewsletterForm)
@@ -0,0 +1,72 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { EMAILS_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs'
import { Button } from 'reactstrap'
export class TopBar extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired,
tableState: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
onCreate = (type) => () => {
const { actions } = this.props
actions.startCreateNotification(type, 'emails', 'emails')
};
goToFiltersTable = () => {
const { actions } = this.props
actions.switchShareSubScreen('emails', EMAILS_SUBSCREENS.FILTERS_TABLE)
};
render () {
const {
t,
tableState: { filter }
} = this.props
const filterName = filter
? `${filter.name} (${t('manageEmailsTab.' + filter.type)})`
: t('manageEmailsTab.allEmails')
return (
<div className="notifications-topbar">
<p className="text-muted align-self-center">
<strong>{t('manageEmailsTab.currentFilter') + ': '}</strong>{" "}
{filterName}
</p>
<div>
<Button
className="btn-icon mr-2"
onClick={this.goToFiltersTable}
>
<i className="lnr lnr-funnel btn-icon-wrapper" />
{t('manageEmailsTab.selectFilter')}
</Button>
<div className="notifications-buttons">
<Button
color="primary"
className="btn-icon"
onClick={this.onCreate(EMAILS_SUBSCREENS.ALERT_FORM)}
>
<i className="lnr lnr-alarm btn-icon-wrapper" />
{t('notificationsTab.newAlert')}
</Button>
{/* <Button
color="primary"
className="btn-icon"
onClick={this.onCreate(EMAILS_SUBSCREENS.NEWSLETTER_FORM)}
>
<i className="lnr lnr-file-add btn-icon-wrapper" />
{t('notificationsTab.newNewsletter')}
</Button> */}
</div>
</div>
</div>
)
}
}
export default translate(['tabsContent'], { wait: true })(TopBar)
@@ -0,0 +1,52 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import ReceiversTable from './ReceiversTable'
import SortableTh from '../../../../common/Table/SortableTh'
import LinkCell from '../../../../common/Table/LinkCell'
class GroupsTable extends ReceiversTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
nameClickAction = (item) => {
this.props.actions.startEditGroup(item)
};
defineColumns () {
//const {t} = this.props;
const colDefs = super.defineColumns()
return {
...colDefs,
'recipientsNumber': {
Header: <SortableTh title='manageRecipientsTab.recipientsNumber' />,
accessor: item => item.recipients.length || '',
width: 140
},
'name': {
Header: <SortableTh title='manageRecipientsTab.groupName' />,
accessor: 'name',
Cell: (row) => {
return (
<LinkCell item={row.original} onClick={this.nameClickAction}>
{row.value}
</LinkCell>
)
}
}
}
}
getColumns () {
return ['selectCheckbox', 'name', 'recipientsNumber', 'subscriptions', 'creationDate', 'active']
}
}
export default translate(['tabsContent'], { wait: true })(GroupsTable)
@@ -0,0 +1,83 @@
import React from 'react'
import PropTypes from 'prop-types'
import TopBar from './TopBar'
import RecipientsTable from './RecipientsTable'
import { RECEIVER_TABLES, RECEIVER_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs'
import {RecipientForm} from './forms/ReceiverForm'
import GroupsTable from './GroupsTable'
import {withRouter} from 'react-router-dom'
import reduxConnect from '../../../../../redux/utils/connect'
import {compose} from 'redux'
import { setDocumentData } from '../../../../../common/helper'
class ManageRecipientsSubTab extends React.Component {
static propTypes = {
shareState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
componentDidMount() {
setDocumentData('title', 'Manage Emails | Share')
}
componentWillUnmount() {
setDocumentData('title')
}
render () {
const { shareState, actions } = this.props
const { subScreenVisible, tableVisible } = shareState.tabs.recipients
const tableState = shareState.tables[tableVisible]
return (
<div className="notifications-tab">
{subScreenVisible === RECEIVER_SUBSCREENS.TABLES &&
<div>
<TopBar
tableVisible={tableVisible}
tables={[RECEIVER_TABLES.RECIPIENTS, RECEIVER_TABLES.GROUPS]}
actions={actions}
/>
{tableVisible === RECEIVER_TABLES.RECIPIENTS &&
<RecipientsTable
tableState={tableState}
actions={actions}
tableActions={actions.shareTables[tableVisible]}
deleteSingleText='recipient'
deleteMultipleText='recipients'
/>
}
{tableVisible === RECEIVER_TABLES.GROUPS &&
<GroupsTable
tableState={tableState}
actions={actions}
tableActions={actions.shareTables[tableVisible]}
deleteSingleText='group'
deleteMultipleText='groups'
/>
}
</div>
}
{(subScreenVisible === RECEIVER_SUBSCREENS.RECIPIENT_FORM || subScreenVisible === RECEIVER_SUBSCREENS.GROUP_FORM) &&
<RecipientForm
formType={subScreenVisible}
shareState={shareState}
actions={actions}
/>
}
</div>
)
}
}
const applyDecorators = compose(
withRouter,
reduxConnect('shareState', ['appState', 'share'])
)
export default applyDecorators(ManageRecipientsSubTab)
@@ -0,0 +1,108 @@
import React from 'react'
import GenericTable from '../common/GenericTable'
import SortableTh from '../../../../common/Table/SortableTh'
import PropTypes from 'prop-types'
import { ButtonGroup, Button } from 'reactstrap'
import { convertUTCtoLocal } from '../../../../../common/helper'
class ReceiversTable extends GenericTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
onActivateButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleActive(tableState.selectedIds, true)
};
onPauseButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleActive(tableState.selectedIds, false)
};
togglerOnAction = (itemId) => {
const { tableActions } = this.props
tableActions.toggleActive([itemId], true)
};
togglerOffAction = (itemId) => {
const { tableActions } = this.props
tableActions.toggleActive([itemId], false)
};
getActionsPanel = () => {
const { t } = this.props
return (
<ButtonGroup className="mb-3">
<Button
color="secondary"
onClick={this.onActivateButtonClick}
>
<i className="fa fa-play mr-1 for-small" />
{t('notificationsTab.activate')}
</Button>
<Button
color="secondary"
onClick={this.onPauseButtonClick}
>
<i className="fa fa-pause for-small mr-1" />
{t('notificationsTab.pause')}
</Button>
<Button
color="secondary"
onClick={this.onDeleteButtonClick}
>
<i className="fa fa-trash for-small mr-1" />
{t('notificationsTab.delete')}
</Button>
</ButtonGroup>
)
};
_formatSubscriptions (subscriptions) {
const { t } = this.props
const result = []
if (subscriptions.alert > 0) {
result.push(`${subscriptions.alert} ${t('notificationsTab.alerts')}`)
}
if (subscriptions.newsletter > 0) {
result.push(`${subscriptions.newsletter} ${t('notificationsTab.newsletters')}`)
}
return result.join(', ')
}
defineColumns () {
const { t } = this.props
const colDefinitions = super.defineColumns()
return {
...colDefinitions,
subscriptions: {
sortable: false,
Header: t('manageRecipientsTab.subscriptions'),
accessor: (item) => this._formatSubscriptions(item.subscriptions),
width: 170
},
creationDate: {
Header: <SortableTh title="manageRecipientsTab.creationDate" />,
accessor: (item) => convertUTCtoLocal(item.creationDate, 'DD MMM YYYY HH:mm'),
width: 100
},
active: this.createTogglerColumn(
'manageRecipientsTab.status',
'active',
'active',
'paused',
this.togglerOnAction,
this.togglerOffAction
)
}
}
}
export default ReceiversTable
@@ -0,0 +1,60 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import ReceiversTable from './ReceiversTable'
import SortableTh from '../../../../common/Table/SortableTh'
import LinkCell from '../../../../common/Table/LinkCell'
class RecipientsTable extends ReceiversTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
nameClickAction = (item) => {
this.props.actions.startEditRecipient(item)
};
defineColumns () {
const {t} = this.props
const colDefs = super.defineColumns()
return {
...colDefs,
'email': {
Header: <SortableTh title='manageRecipientsTab.email' />,
accessor: 'email',
width: 170
},
'groups': {
sortable: false,
Header: t('manageRecipientsTab.groups'),
accessor: item => item.groups.map(group => group.name).join(', '),
width: 170
},
'name': {
Header: <SortableTh title='manageRecipientsTab.name' />,
accessor: 'name',
Cell: (row) => {
const {original} = row
const name = `${original.firstName} ${original.lastName}`
return (
<LinkCell item={original} onClick={this.nameClickAction}>
{name}
</LinkCell>
)
}
}
}
}
getColumns () {
return ['selectCheckbox', 'name', 'email', 'groups', 'subscriptions', 'creationDate', 'active']
}
}
export default translate(['tabsContent'], { wait: true })(RecipientsTable)
@@ -0,0 +1,77 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { Button, Input, InputGroup, InputGroupAddon } from 'reactstrap'
const INPUT_THROTTLE_TIME = 300
export class TableFilter extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired,
onFilterRequest: PropTypes.func.isRequired
};
constructor () {
super()
this.state = {
value: ''
}
}
onFilter = (event) => {
this._onFilterImpl(event.target.value)
};
_onFilterImpl (filterValue) {
const { onFilterRequest } = this.props
this.setState({ value: filterValue })
if (this.inputDelay) {
clearTimeout(this.inputDelay)
}
this.inputDelay = setTimeout(() => {
onFilterRequest(filterValue)
}, INPUT_THROTTLE_TIME)
}
onClear = () => {
const value = this.state.value
value && this._onFilterImpl('')
};
render () {
const { type } = this.props
const value = this.state.value
const hasValue = !!value
const iconClasses = classnames('fa', {
'fa-search': !hasValue,
'fa-times': hasValue
})
const placeholder = `Find ${type}`
return (
<InputGroup className="mb-3">
<Input
type="text"
placeholder={placeholder}
value={value}
onChange={this.onFilter}
/>
<InputGroupAddon addonType="append">
<Button color="primary" onClick={this.onClear}>
<i className={iconClasses}></i>
</Button>
</InputGroupAddon>
{/* <button
className="cw-grid__filter-button"
type="button"
onClick={this.onClear}
>
<i className={iconClasses} />
</button> */}
</InputGroup>
)
}
}
export default TableFilter
@@ -0,0 +1,77 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import TableFilter from './TableFilter'
import TableSwitcher from '../common/TableSwitcher/TableSwitcher'
import { Button } from 'reactstrap'
export class TopBar extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
tables: PropTypes.array.isRequired,
tableVisible: PropTypes.string.isRequired,
actions: PropTypes.object.isRequired
};
onNewRecipient = () => {
const { actions } = this.props
actions.startCreateRecipient()
};
onNewGroup = () => {
const { actions } = this.props
actions.startCreateGroup()
};
onFilterRequest = (filter) => {
this.loadTable({ filter })
};
loadTable = (params) => {
const { tableVisible: type } = this.props
this.props.actions.shareTables[type].loadTable(params || null)
};
render () {
const { t, tables, tableVisible, actions } = this.props
return (
<Fragment>
<div className="notifications-topbar align-items-center">
<TableSwitcher
tables={tables}
tableVisible={tableVisible}
subTab="recipients"
switchTable={actions.switchShareTable}
loadTable={this.loadTable}
/>
<div className="notifications-buttons">
<Button
color="primary"
className="btn-icon mr-2"
onClick={this.onNewRecipient}
>
<i className="lnr lnr-location for-small btn-icon-wrapper" />
{t('manageRecipientsTab.newRecipient')}
</Button>
<Button
color="primary"
className="btn-icon"
onClick={this.onNewGroup}
>
<i className="lnr lnr-users for-small btn-icon-wrapper" />
{t('manageRecipientsTab.newGroup')}
</Button>
</div>
</div>
<TableFilter
type={tableVisible}
onFilterRequest={this.onFilterRequest}
/>
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(TopBar)
@@ -0,0 +1,61 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import InputField from './InputField'
import { Card, CardBody, CardTitle, Form, FormGroup, Input, Label } from 'reactstrap'
export class BasicGroupInfo extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
formActions: PropTypes.object.isRequired
};
onChangeFor = (field) => (event) => {
const { formActions } = this.props
formActions.changeField(field, event.target.value)
};
render () {
const { t, item } = this.props
return (
<Card>
<CardBody>
<CardTitle>{t('manageRecipientsTab.form.group.basicInfo')}</CardTitle>
<Form>
<InputField
formType="group"
field="name"
value={item.name}
onChangeFor={this.onChangeFor}
/>
<FormGroup>
<Label>
{t('manageRecipientsTab.form.group.description')}
</Label>
<Input
type="textarea"
rows="5"
onChange={this.onChangeFor('description')}
value={item.description}
/>
</FormGroup>
</Form>
<hr />
{!!item.recipients && (
<p>
{t('manageRecipientsTab.form.group.recipientsNumber')}:{' '}
{item.recipients.length}
</p>
)}
</CardBody>
</Card>
)
}
}
export default translate(['tabsContent'], { wait: true })(BasicGroupInfo)
@@ -0,0 +1,54 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import InputField from './InputField'
import { Card, CardBody, CardTitle, Form } from 'reactstrap'
export class BasicRecipientInfo extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
item: PropTypes.object.isRequired,
formActions: PropTypes.object.isRequired
}
onChangeFor = (field) => (event) => {
const { formActions } = this.props
formActions.changeField(field, event.target.value)
}
render() {
const { item, t } = this.props
return (
<Card>
<CardBody>
<CardTitle>{t('manageRecipientsTab.form.recipient.basicInfo')}</CardTitle>
<Form>
<InputField
formType="recipient"
field="firstName"
value={item.firstName}
onChangeFor={this.onChangeFor}
/>
<InputField
formType="recipient"
field="lastName"
value={item.lastName}
onChangeFor={this.onChangeFor}
/>
<InputField
formType="recipient"
field="email"
value={item.email}
onChangeFor={this.onChangeFor}
/>
</Form>
</CardBody>
</Card>
)
}
}
export default translate(['tabsContent'], { wait: true })(BasicRecipientInfo)
@@ -0,0 +1,27 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
export class BreadCrumbs extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
onBack: PropTypes.func.isRequired
};
render () {
const { t, title, onBack } = this.props
return (
<div>
<a href="#" onClick={onBack}>
{t('tableSwitcher.recipients')}
</a>
<span> &gt; {title}</span>
</div>
)
}
}
export default translate(['tabsContent'], { wait: true })(BreadCrumbs)
@@ -0,0 +1,119 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import Toggler from '../../../../../common/Table/Toggler'
import { Button, Card, CardBody, CardTitle, Label } from 'reactstrap'
import { convertUTCtoLocal } from '../../../../../../common/helper'
export class FormTopBar extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
formType: PropTypes.string.isRequired,
receiver: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
togglerAction = () => {
const { actions, formType } = this.props
actions.shareForms[formType].toggleActive()
}
onBack = () => {
this.props.actions.switchShareSubScreen('recipients', 'tables')
}
onDelete = () => {
const { formType, actions } = this.props
actions.shareForms[formType].confirmDelete()
}
onSave = () => {
const { actions, formType } = this.props
actions.shareForms[formType].saveReceiver()
}
render() {
const { t, formType, receiver } = this.props
const hasItem = !!receiver.id
const trPath = 'manageRecipientsTab.form'
let title = t(`${trPath}.${formType}.unsaved`)
if (hasItem) {
title =
formType === 'group'
? receiver.name
: `${receiver.firstName} ${receiver.lastName}`
}
return (
<Fragment>
<Button
className="btn-wide mb-3"
size="sm"
color="info"
onClick={this.onBack}
>
<i className="lnr lnr-chevron-left"> </i>
</Button>
<Card className="main-card mb-3">
<CardBody>
<CardTitle>{title}</CardTitle>
<div className="d-flex justify-content-between flex-wrap align-items-center">
<div>
<Label className="mr-2">
{t(`${trPath}.${formType}.nameStatus`)}
</Label>
<Toggler
id={receiver.id}
turnOnAction={this.togglerAction}
turnOffAction={this.togglerAction}
state={receiver.active}
enabledText="active"
disabledText="paused"
/>
</div>
<div>
{hasItem && (
<Button
color="danger"
className="btn-icon mr-2"
onClick={this.onDelete}
>
<i className="lnr lnr-trash btn-icon-wrapper"></i>
{t(`${trPath}.${formType}.deleteButton`)}
</Button>
)}
<Button
color="secondary"
className="btn-icon mr-2"
onClick={this.onBack}
>
<i className="lnr lnr-cross btn-icon-wrapper"></i>
{t(`${trPath}.cancel`)}
</Button>
<Button
color="success"
className="btn-icon mr-2"
onClick={this.onSave}
>
<i className="lnr lnr-checkmark-circle btn-icon-wrapper" />
{t(`${trPath}.save`)}
</Button>
</div>
</div>
{hasItem && receiver.creationDate && (
<p className="mt-1">
{t(`${trPath}.${formType}.creationDate`)}:
{convertUTCtoLocal(receiver.creationDate, 'DD MMM YYYY HH:mm')}
</p>
)}
</CardBody>
</Card>
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(FormTopBar)
@@ -0,0 +1,29 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { FormGroup, Input, Label } from 'reactstrap'
export class InputField extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
formType: PropTypes.string.isRequired,
field: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onChangeFor: PropTypes.func.isRequired
};
render () {
const { t, formType, field, value, onChangeFor } = this.props
const trPath = `manageRecipientsTab.form.${formType}`
return (
<FormGroup>
<Label>{t(`${trPath}.${field}`)}</Label>
<Input type="text" onChange={onChangeFor(field)} value={value} />
</FormGroup>
)
}
}
export default translate(['tabsContent'], { wait: true })(InputField)
@@ -0,0 +1,138 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import FormTopBar from './FormTopBar'
import BasicRecipientInfo from './BasicRecipientInfo'
import BasicGroupInfo from './BasicGroupInfo'
import TablesTabs from './TablesTabs'
import EmailHistoryTable from './tables/EmailHistoryTable'
import DeletePopup from '../../common/DeletePopup'
import {
RECIPIENT_FORM_TABLES,
GROUP_FORM_TABLES,
RECEIVER_SUBSCREENS
} from '../../../../../../redux/modules/appState/share/tabs'
import ReceiverSubscriptionsTable from './tables/ReceiverSubscriptionsTable'
import ReceiverGroupsTable from './tables/ReceiverGroupsTable'
import ReceiverRecipientsTable from './tables/ReceiverRecipientsTable'
import { Card, CardBody, CardHeader, Col, Nav, Row } from 'reactstrap'
export class RecipientForm extends React.Component {
static propTypes = {
formType: PropTypes.string.isRequired,
shareState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
chooseTableTab = (tab) => {
const { actions, formType } = this.props
actions.shareForms[formType].chooseTableTab(tab)
}
render() {
const { formType, shareState, actions } = this.props
const formState = shareState.forms[formType] // receiver
const formActions = actions.shareForms[formType]
let allTabs = formState.tabs.all
if (!formState.id) {
allTabs = allTabs.filter(
(tab) => tab !== RECIPIENT_FORM_TABLES.EMAIL_HISTORY
)
}
const activeTab = formState.tabs.active
const tableState = shareState.tables.receiverForm[activeTab]
const tableActions = actions.shareTables.receiverForm[activeTab]
const deleteText =
formType === RECEIVER_SUBSCREENS.GROUP_FORM ? 'group' : 'recipient'
return (
<Fragment>
<FormTopBar
formType={formType}
receiver={formState}
actions={actions}
/>
<Row>
<Col lg="4">
{formType === RECEIVER_SUBSCREENS.RECIPIENT_FORM && (
<BasicRecipientInfo item={formState} formActions={formActions} />
)}
{formType === RECEIVER_SUBSCREENS.GROUP_FORM && (
<BasicGroupInfo item={formState} formActions={formActions} />
)}
</Col>
<Col lg="8">
<Card className="mb-3">
<CardHeader>
<Nav justified>
<TablesTabs
tabs={allTabs}
activeTab={activeTab}
chooseTableTab={this.chooseTableTab}
/>
</Nav>
</CardHeader>
<CardBody>
{activeTab === RECIPIENT_FORM_TABLES.SUBSCRIPTIONS && (
<ReceiverSubscriptionsTable
tableState={tableState}
actions={actions}
tableActions={tableActions}
receiver={formState}
formActions={formActions}
/>
)}
{activeTab === RECIPIENT_FORM_TABLES.GROUPS && (
<ReceiverGroupsTable
tableState={tableState}
actions={actions}
tableActions={tableActions}
receiver={formState}
formActions={formActions}
/>
)}
{activeTab === RECIPIENT_FORM_TABLES.EMAIL_HISTORY && (
<EmailHistoryTable
type={activeTab}
tableState={tableState}
actions={actions}
tableActions={tableActions}
receiver={formState}
/>
)}
{activeTab === GROUP_FORM_TABLES.RECIPIENTS && (
<ReceiverRecipientsTable
tableState={tableState}
actions={actions}
tableActions={tableActions}
receiver={formState}
formActions={formActions}
/>
)}
</CardBody>
</Card>
</Col>
</Row>
{formState.isDeletePopupVisible && (
<DeletePopup
actions={formActions}
idsToDelete={[formState.id]}
deleteSingleText={deleteText}
/>
)}
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(RecipientForm)
@@ -0,0 +1,35 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { NavItem, NavLink } from 'reactstrap'
export class TablesTabs extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
tabs: PropTypes.array.isRequired,
activeTab: PropTypes.string.isRequired,
chooseTableTab: PropTypes.func.isRequired
}
chooseTableTab = (tab) => () => {
this.props.chooseTableTab(tab)
}
render() {
const { t, tabs, activeTab } = this.props
return tabs.map((tab, i) => (
<NavItem key={tab}>
<NavLink
key={`table-tab-${i}`}
active={tab === activeTab}
onClick={this.chooseTableTab(tab)}
>
{t(`manageRecipientsTab.tables.${tab}`)}
</NavLink>
</NavItem>
))
}
}
export default translate(['tabsContent'], { wait: true })(TablesTabs)
@@ -0,0 +1,45 @@
import React from 'react'
import PropTypes from 'prop-types'
import {translate} from 'react-i18next'
import ReceiverFormTable from './ReceiverFormTable'
import SortableTh from '../../../../../../common/Table/SortableTh'
import { convertUTCtoLocal } from '../../../../../../../common/helper'
export class EmailHistoryTable extends ReceiverFormTable {
static propTypes = {
t: PropTypes.func.isRequired,
receiver: PropTypes.object.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired
};
defineColumns () {
const {t} = this.props
return {
...super.defineColumns(),
'ScheduledTimes': {
sortable: false,
Header: t('notificationsTab.ScheduledTimes'),
accessor: item => this.scheduleFormat(item.schedule),
width: 170
},
'sentTime': {
Header: <SortableTh title='notificationsTab.sentTime' />,
accessor: item => convertUTCtoLocal(item.sentTime, 'DD MMM YYYY HH:mm'),
width: 170
}
}
}
getColumns () {
return ['name', 'type', 'ScheduledTimes', 'sentTime']
}
noCard () {
return true
}
}
export default translate(['tabsContent'], { wait: true })(EmailHistoryTable)
@@ -0,0 +1,82 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import TableFilter from '../../TableFilter'
import { Nav, NavItem, NavLink } from 'reactstrap'
class FormTableTopBar extends React.Component {
static propTypes = {
tableActions: PropTypes.object.isRequired,
statusFilter: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
yesText: PropTypes.string.isRequired,
noText: PropTypes.string.isRequired,
allText: PropTypes.string.isRequired,
receiver: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
}
onFilterRequest = (filter) => {
const { tableActions, receiver } = this.props
tableActions.loadTable({ filter }, receiver)
}
onStatusFilter = (statusFilter) => {
return () => {
const { tableActions, receiver } = this.props
tableActions.loadTable({ statusFilter }, receiver)
}
}
render() {
const {
type,
t,
yesText,
noText,
allText,
statusFilter,
receiver
} = this.props
return (
<div>
{receiver.id && (
<Nav pills justified>
<NavItem>
<NavLink
className="d-block"
active={statusFilter === 'all'}
onClick={this.onStatusFilter('all')}
>
{t('manageRecipientsTab.' + allText)}
</NavLink>
</NavItem>
<NavItem>
<NavLink
className="d-block"
active={statusFilter === 'yes'}
onClick={this.onStatusFilter('yes')}
>
{t('manageRecipientsTab.' + yesText)}
</NavLink>
</NavItem>
<NavItem>
<NavLink
className="d-block"
active={statusFilter === 'no'}
onClick={this.onStatusFilter('no')}
>
{t('manageRecipientsTab.' + noText)}
</NavLink>
</NavItem>
</Nav>
)}
<TableFilter type={type} onFilterRequest={this.onFilterRequest} />
</div>
)
}
}
export default translate(['tabsContent'], { wait: true })(FormTableTopBar)
@@ -0,0 +1,45 @@
import React from 'react'
import PropTypes from 'prop-types'
import GenericTable from '../../../common/GenericTable'
import SortableTh from '../../../../../../common/Table/SortableTh'
class ReceiverFormTable extends GenericTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
receiver: PropTypes.object.isRequired
};
fetchData = (page, pageSize, sorted) => {
const { tableActions, receiver } = this.props
const params = {
page: page + 1,
limit: pageSize
}
if (sorted.length) {
const sortedField = sorted[0]
params['sortField'] = sortedField.id
params['sortDirection'] = sortedField.desc ? 'desc' : 'asc'
}
tableActions.loadTable(params, receiver)
};
defineColumns () {
const {t} = this.props
const colDefs = super.defineColumns()
return {
...colDefs,
'active': {
Header: <SortableTh title='notificationsTab.status' />,
accessor: item => item.active ? t('notificationsTab.active') : t('notificationsTab.paused'),
width: 100
}
}
}
}
export default ReceiverFormTable
@@ -0,0 +1,108 @@
import React from 'react'
import PropTypes from 'prop-types'
import {translate} from 'react-i18next'
import ReceiverFormTable from './ReceiverFormTable'
import SortableTh from '../../../../../../common/Table/SortableTh'
import LinkCell from '../../../../../../common/Table/LinkCell'
import FormTableTopBar from './FormTableTopBar'
export class ReceiverGroupsTable extends ReceiverFormTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
receiver: PropTypes.object.isRequired,
formActions: PropTypes.object.isRequired
};
togglerOnAction = (itemId) => {
this.props.formActions.toggleGroup(itemId, true)
this.props.tableActions.toggleEnrolled(itemId, true)
};
togglerOffAction = (itemId) => {
this.props.formActions.toggleGroup(itemId, false)
this.props.tableActions.toggleEnrolled(itemId, false)
};
_formatSubscriptions (subscriptions) {
console.log('format subsc', subscriptions)
const result = []
if (subscriptions.alert > 0) {
result.push(`${subscriptions.alert} Alerts`)
}
if (subscriptions.newsletter > 0) {
result.push(`${subscriptions.newsletter} Newsletters`)
}
return result.join(', ')
};
_formatRecipients (number) {
if (number) {
if (number === 1) {
return '1 Recipient'
} else {
return number + ' Recipients'
}
}
return ''
}
defineColumns () {
const {t} = this.props
return {
...super.defineColumns(),
'groupName': {
Header: <SortableTh title='manageRecipientsTab.groupName' />,
accessor: 'name',
Cell: (row) => {
return (
<LinkCell item={row.original} onClick={this.nameClickAction}>
{row.value}
</LinkCell>
)
}
},
'enrolled': this.createTogglerColumn('manageRecipientsTab.form.recipient.enroll', 'enrolled', 'yes', 'no', this.togglerOnAction, this.togglerOffAction),
'subscriptions': {
sortable: false,
Header: t('manageRecipientsTab.subscriptions'),
accessor: item => this._formatSubscriptions(item.subscriptions),
width: 170
},
'recipients': {
sortable: false,
Header: t('manageRecipientsTab.recipients'),
accessor: item => this._formatRecipients(item.recipients.length),
width: 170
}
}
}
getColumns () {
return ['groupName', 'subscriptions', 'recipients', 'active', 'enrolled']
}
noCard () {
return true
}
getActionsPanel () {
const {tableState, tableActions, receiver} = this.props
return (
<FormTableTopBar
tableActions={tableActions}
statusFilter={tableState.statusFilter}
receiver={receiver}
type="groups"
yesText="Enrolled"
noText="NotEnrolled"
allText="All"
/>
)
}
}
export default translate(['tabsContent'], { wait: true })(ReceiverGroupsTable)
@@ -0,0 +1,75 @@
import React from 'react'
import PropTypes from 'prop-types'
import {translate} from 'react-i18next'
import ReceiverFormTable from './ReceiverFormTable'
import SortableTh from '../../../../../../common/Table/SortableTh'
import FormTableTopBar from './FormTableTopBar'
import { convertUTCtoLocal } from '../../../../../../../common/helper'
export class ReceiverRecipientsTable extends ReceiverFormTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
receiver: PropTypes.object.isRequired,
formActions: PropTypes.object.isRequired
};
togglerOnAction = (itemId) => {
this.props.formActions.toggleRecipient(itemId, true)
this.props.tableActions.toggleEnrolled(itemId, true)
};
togglerOffAction = (itemId) => {
this.props.formActions.toggleRecipient(itemId, false)
this.props.tableActions.toggleEnrolled(itemId, false)
};
defineColumns () {
const {t} = this.props
return {
...super.defineColumns(),
'enrolled': this.createTogglerColumn('manageRecipientsTab.form.recipient.enroll', 'enrolled', 'yes', 'no', this.togglerOnAction, this.togglerOffAction),
'name': {
Header: <SortableTh title='manageRecipientsTab.name' />,
accessor: item => `${item.firstName} ${item.lastName}`
},
'email': {
Header: <SortableTh title='manageRecipientsTab.email' />,
accessor: 'email',
width: 170
},
'addedDate': {
Header: t('manageRecipientsTab.form.group.addedDate'),
accessor: item => item.creationDate ? convertUTCtoLocal(item.creationDate, 'DD MMM YYYY HH:mm') : '',
width: 170
}
}
}
getColumns () {
return ['name', 'email', 'addedDate', 'active', 'enrolled']
}
noCard () {
return true
}
getActionsPanel () {
const {tableState, tableActions, receiver} = this.props
return (
<FormTableTopBar
tableActions={tableActions}
statusFilter={tableState.statusFilter}
receiver={receiver}
type="recipients"
yesText="Enrolled"
noText="NotEnrolled"
allText="All"
/>
)
}
}
export default translate(['tabsContent'], { wait: true })(ReceiverRecipientsTable)
@@ -0,0 +1,58 @@
import React from 'react'
import PropTypes from 'prop-types'
import {translate} from 'react-i18next'
import ReceiverFormTable from './ReceiverFormTable'
import FormTableTopBar from './FormTableTopBar'
export class ReceiverSubscriptionsTable extends ReceiverFormTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
receiver: PropTypes.object.isRequired,
formActions: PropTypes.object.isRequired
};
togglerOnAction = (itemId) => {
this.props.formActions.toggleSubscription(itemId, true)
this.props.tableActions.toggleSubscribed(itemId, true)
};
togglerOffAction = (itemId) => {
this.props.formActions.toggleSubscription(itemId, false)
this.props.tableActions.toggleSubscribed(itemId, false)
};
defineColumns () {
return {
...super.defineColumns(),
'subscribed': this.createTogglerColumn('notificationsTab.action', 'subscribed', 'subscribed', 'unsubscribed', this.togglerOnAction, this.togglerOffAction)
}
}
getColumns () {
return ['name', 'type', 'ScheduledTimes', 'active', 'subscribed']
}
noCard () {
return true
}
getActionsPanel () {
const {tableState, tableActions, receiver} = this.props
return (
<FormTableTopBar
tableActions={tableActions}
statusFilter={tableState.statusFilter}
receiver={receiver}
type="subscriptions"
yesText="Subscribed"
noText="Unsubscribed"
allText="All"
/>
)
}
}
export default translate(['tabsContent'], { wait: true })(ReceiverSubscriptionsTable)
@@ -0,0 +1,146 @@
import React, { Fragment } from 'react'
import GenericTable from '../common/GenericTable'
import { translate } from 'react-i18next'
import PropTypes from 'prop-types'
import { NOTIFICATION_TABLES } from '../../../../../redux/modules/appState/share/tabs'
import SortableTh from '../../../../common/Table/SortableTh'
import { ButtonGroup, Button } from 'reactstrap'
export class MyEmailsTable extends GenericTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
restrictions: PropTypes.object,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
togglerOnAction = (itemId) => {
this.props.tableActions.toggleActive([itemId], true)
};
togglerOffAction = (itemId) => {
this.props.tableActions.toggleActive([itemId], false)
};
nameClickAction = (item) => {
const { actions } = this.props
actions.startEditNotification(item, NOTIFICATION_TABLES.MY_EMAILS)
};
onPublishButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.togglePublish(tableState.selectedIds, true)
};
onUnPublishButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.togglePublish(tableState.selectedIds, false)
};
_recipientsFormat (recipients) {
if (recipients.length === 1) {
return recipients[0].email
}
return `${recipients.length} ${this.props.t(
'notificationsTab.recipients'
)}`
}
defineColumns () {
const { t } = this.props
const colDefinitions = super.defineColumns()
return {
...colDefinitions,
active: this.createTogglerColumn(
'notificationsTab.action',
'active',
'active',
'paused',
this.togglerOnAction,
this.togglerOffAction
),
Recipients: {
sortable: false,
Header: t('notificationsTab.Recipients'),
accessor: (item) => this._recipientsFormat(item.recipients),
width: 110
},
published: {
Header: <SortableTh title="notificationsTab.published" />,
accessor: (item) =>
item.published
? t('common:commonWords.Yes')
: t('common:commonWords.No'),
width: 100
}
}
}
getColumns () {
return [
'selectCheckbox',
'name',
'type',
'published',
'ScheduledTimes',
'sourcesCount',
'Recipients',
'active',
'delete'
]
}
getActionsPanel () {
const { t, restrictions } = this.props
return (
<Fragment>
{this.getRestrictions(restrictions)}
<ButtonGroup className="mb-3">
<Button
color="secondary"
onClick={this.onActivateButtonClick}
>
<i className="fa fa-play for-small mr-1"> </i>{" "}
{t('notificationsTab.activate')}
</Button>
<Button
color="secondary"
onClick={this.onPauseButtonClick}
>
<i className="fa fa-pause for-small mr-1"> </i>{" "}
{t('notificationsTab.pause')}
</Button>
<Button
color="secondary"
onClick={this.onDeleteButtonClick}
>
<i className="fa fa-trash for-small mr-1"> </i>{" "}
{t('notificationsTab.delete')}
</Button>
<Button
color="secondary"
onClick={this.onPublishButtonClick}
>
<i className="fa fa-upload for-small mr-1"> </i>{" "}
{t('notificationsTab.publish')}
</Button>
<Button
color="secondary"
onClick={this.onUnPublishButtonClick}
>
<i className="fa fa-ban for-small mr-1"> </i>{" "}
{t('notificationsTab.unpublish')}
</Button>
</ButtonGroup>
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(MyEmailsTable)
@@ -0,0 +1,26 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { Button } from 'reactstrap'
class Navigation extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired
};
backToTables = () => {
this.props.actions.switchShareSubScreen('notifications', 'tables')
};
render () {
return (
<Button className="btn-wide mb-2" size="sm" color="info" onClick={this.backToTables}>
<i className="lnr lnr-chevron-left"> </i>
</Button>
)
}
}
export default translate(['tabsContent'], { wait: true })(Navigation)
@@ -0,0 +1,106 @@
import React from 'react'
import PropTypes from 'prop-types'
import TopBar from './TopBar'
import Navigation from './Navigation'
import AlertForm from './forms/AlertForm'
import {NOTIFICATION_TABLES, NOTIFICATION_SUBSCREENS} from '../../../../../redux/modules/appState/share/tabs'
import MyEmailsTable from './MyEmailsTable'
import PublishedEmailsTable from './PublishedEmailsTable'
import {withRouter} from 'react-router-dom'
import reduxConnect from '../../../../../redux/utils/connect'
import {compose} from 'redux'
import { setDocumentData } from '../../../../../common/helper'
class NotificationsSubTab extends React.Component {
static propTypes = {
store: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
_shareState = () => this.props.store.appState.share;
_authState = () => this.props.store.common.auth;
componentDidMount() {
setDocumentData('title', 'Alerts | Share')
}
componentWillUnmount() {
setDocumentData('title')
}
render () {
const { actions } = this.props
const shareState = this._shareState()
const {user: {restrictions}} = this._authState()
const { subScreenVisible, tableVisible } = shareState.tabs.notifications
return (
<div className="notifications-tab">
{subScreenVisible === NOTIFICATION_SUBSCREENS.TABLES &&
<div>
<TopBar
tables={[NOTIFICATION_TABLES.MY_EMAILS, NOTIFICATION_TABLES.PUBLISHED]}
tableVisible={tableVisible}
actions={actions}
/>
{tableVisible === NOTIFICATION_TABLES.MY_EMAILS &&
<MyEmailsTable
tableState={shareState.tables[tableVisible]}
restrictions={restrictions && restrictions.limits}
actions={actions}
tableActions={actions.shareTables[tableVisible]}
deleteSingleText='alert'
deleteMultipleText='alerts'
/>
}
{tableVisible === NOTIFICATION_TABLES.PUBLISHED &&
<PublishedEmailsTable
tableState={shareState.tables[tableVisible]}
restrictions={restrictions && restrictions.limits}
actions={actions}
tableActions={actions.shareTables[tableVisible]}
deleteSingleText='alert'
deleteMultipleText='alerts'
/>
}
</div>
}
{(subScreenVisible === NOTIFICATION_SUBSCREENS.ALERT_FORM || subScreenVisible === NOTIFICATION_SUBSCREENS.NEWSLETTER_FORM) &&
<Navigation actions={actions} />
}
{subScreenVisible === NOTIFICATION_SUBSCREENS.ALERT_FORM &&
<AlertForm
state={shareState.forms.alert}
switchShareSubScreen={actions.switchShareSubScreen}
actions={actions.shareForms.alert}
/>
}
{/* {subScreenVisible === NOTIFICATION_SUBSCREENS.NEWSLETTER_FORM &&
<NewsletterForm
state={shareState.forms.newsletter}
switchShareSubScreen={actions.switchShareSubScreen}
actions={actions.shareForms.newsletter}
/>
} */}
</div>
)
}
}
const applyDecorators = compose(
withRouter,
reduxConnect()
)
export default applyDecorators(NotificationsSubTab)
@@ -0,0 +1,89 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import GenericTable from '../common/GenericTable'
import {NOTIFICATION_TABLES} from '../../../../../redux/modules/appState/share/tabs'
import SortableTh from '../../../../common/Table/SortableTh'
import { Button, ButtonGroup } from 'reactstrap'
class PublishedEmailsTable extends GenericTable {
static propTypes = {
t: PropTypes.func.isRequired,
tableState: PropTypes.object.isRequired,
restrictions: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
onSubscribeButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleSubscribe(tableState.selectedIds, true)
};
onUnSubscribeButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleSubscribe(tableState.selectedIds, false)
};
togglerOnAction = (itemId) => {
this.props.tableActions.toggleSubscribe([itemId], true)
};
togglerOffAction = (itemId) => {
const {tableState, tableActions, actions} = this.props
const notification = tableState.data.find(item => item.id === itemId)
if (notification.allowUnsubscribe) {
tableActions.toggleSubscribe([itemId], false)
} else {
actions.addAlert({type: 'error', transKey: 'cannotUnsubscribe'})
}
};
nameClickAction = (item) => {
const { actions } = this.props
actions.startEditNotification(item, NOTIFICATION_TABLES.PUBLISHED)
};
defineColumns () {
const {t} = this.props
const colDefinitions = super.defineColumns()
return {
...colDefinitions,
'subscribed': this.createTogglerColumn('notificationsTab.action', 'subscribed', 'subscribed', 'unsubscribed', this.togglerOnAction, this.togglerOffAction),
'active': {
Header: <SortableTh title='notificationsTab.status' />,
accessor: item => item.active ? t('notificationsTab.active') : t('notificationsTab.paused'),
width: 100
}
}
};
getColumns () {
return ['selectCheckbox', 'name', 'type', 'owner', 'ScheduledTimes', 'active', 'subscribed']
}
getActionsPanel () {
const {t, restrictions} = this.props
return (
<Fragment>
{this.getRestrictions(restrictions)}
<ButtonGroup className="mb-3">
<Button onClick={this.onSubscribeButtonClick}>
<i className="fa fa-envelope for-small mr-1" /> {t('notificationsTab.subscribe')}
</Button>
<Button onClick={this.onUnSubscribeButtonClick}>
<i className="fa fa-times for-small mr-1" /> {t('notificationsTab.unsubscribe')}
</Button>
</ButtonGroup>
</Fragment>
)
}
}
export default translate(['tabsContent'], { wait: true })(PublishedEmailsTable)
@@ -0,0 +1,50 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import TableSwitcher from '../common/TableSwitcher/TableSwitcher'
import { NOTIFICATION_SUBSCREENS } from '../../../../../redux/modules/appState/share/tabs'
import { Button } from 'reactstrap'
class TopBar extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired,
tables: PropTypes.array.isRequired,
tableVisible: PropTypes.string.isRequired,
t: PropTypes.func.isRequired
}
onCreate = (type) => () => {
const { actions, tableVisible } = this.props
actions.startCreateNotification(type, tableVisible)
}
loadTable = (type) => {
this.props.actions.shareTables[type].loadTable(null)
}
render() {
const { t, tables, tableVisible, actions } = this.props
return (
<div className="notifications-topbar">
<div className="notifications-topbar_buttons_wrap">
<TableSwitcher
tables={tables}
tableVisible={tableVisible}
subTab="notifications"
switchTable={actions.switchShareTable}
loadTable={this.loadTable}
/>
<div className="notifications-buttons">
<Button className="btn-icon" color="primary" onClick={this.onCreate(NOTIFICATION_SUBSCREENS.ALERT_FORM)}>
<i className="lnr lnr-alarm btn-icon-wrapper"></i>
{t('notificationsTab.newAlert')}
</Button>
</div>
</div>
</div>
)
}
}
export default translate(['tabsContent'], { wait: true })(TopBar)
@@ -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
@@ -0,0 +1,66 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route, Switch, withRouter } from 'react-router-dom';
import SubTabWrapper from '../../AppHeader/SubTabWrapper';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import NotificationsSubTab from './NotificatoinsSubTab/NotificationsSubTab';
import ManageRecipientsSubTab from './ManageRecipientsSubTub/ManageRecipientsSubTab';
import ManageEmailsSubTab from './ManageEmailsSubTab/ManageEmailsSubTab';
import ExportSubTab from './ExportSubTab/ExportSubTab';
class ShareTab extends React.Component {
static propTypes = {
activeTabName: PropTypes.string,
subTabs: PropTypes.any,
match: PropTypes.object,
isMaster: PropTypes.bool
};
render() {
const { subTabs, isMaster, match, activeTabName } = this.props;
return (
<CSSTransitionGroup
component="div"
transitionName="TabsAnimation"
transitionAppear
transitionAppearTimeout={0}
transitionEnter={false}
transitionLeave={false}
>
<SubTabWrapper activeTabName={activeTabName} subTabs={subTabs}>
<Switch>
<Route
exact
path={`${match.url}/notifications`}
component={NotificationsSubTab}
/>
<Route
exact
path={`${match.url}/export`}
component={ExportSubTab}
/>
{isMaster
? [
<Route
exact
key={`${match.url}/manage-recipients`}
path={`${match.url}/manage-recipients`}
component={ManageRecipientsSubTab}
/>,
<Route
exact
key={`${match.url}/manage-emails`}
path={`${match.url}/manage-emails`}
component={ManageEmailsSubTab}
/>
]
: null}
<Redirect to={`${match.url}/notifications`} />
</Switch>
</SubTabWrapper>
</CSSTransitionGroup>
);
}
}
export default withRouter(ShareTab);
@@ -0,0 +1,60 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate, Interpolate } from 'react-i18next'
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'
export class DeletePopup extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
actions: PropTypes.object.isRequired,
idsToDelete: PropTypes.array.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
}
hidePopup = () => {
const { actions } = this.props
actions.cancelDelete()
}
onSubmit = () => {
const { actions, idsToDelete } = this.props
actions.deleteItems(idsToDelete)
}
render() {
const { t, idsToDelete, deleteSingleText, deleteMultipleText } = this.props
const length = Object.keys(idsToDelete).length
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static">
<ModalHeader toggle={this.hidePopup}>
{t('common:commonWords.Confirm')}
</ModalHeader>
<ModalBody>
<p>
{length === 1 ? (
t('tabsContent:deletePopup.' + deleteSingleText)
) : (
<Interpolate
i18nKey={'tabsContent:deletePopup.' + deleteMultipleText}
count={length}
/>
)}
</p>
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('common:commonWords.Cancel')}
</Button>
<Button color="danger" onClick={this.onSubmit}>
{t('common:commonWords.Delete')}
</Button>
</ModalFooter>
</Modal>
)
}
}
export default translate(['common', 'tabsContent'], { wait: true })(DeletePopup)
@@ -0,0 +1,299 @@
import React from 'react'
import PropTypes from 'prop-types'
import Table from '../../../../common/Table/Table'
import LinkCell from '../../../../common/Table/LinkCell'
import CheckboxCell from '../../../../common/Table/CheckboxCell'
import SortableTh from '../../../../common/Table/SortableTh'
import DeletePopup from './DeletePopup'
import Toggler from '../../../../common/Table/Toggler'
import { addOrdinalSuffix, padLeft } from '../../../../../common/StringUtils'
import DeleteButton from '../../../../common/Table/DeleteButton'
import Restrictions from '../../../../common/Restrictions/Restrictions'
export class GenericTable extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
type: PropTypes.string.isRequired,
tableState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
tableActions: PropTypes.object.isRequired,
deleteSingleText: PropTypes.string.isRequired,
deleteMultipleText: PropTypes.string.isRequired
};
onDeleteButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.confirmDelete(tableState.selectedIds)
};
fetchData = (page, pageSize, sorted) => {
const { tableActions } = this.props
const params = {
page: page + 1,
limit: pageSize
}
if (sorted.length) {
const sortedField = sorted[0]
params['sortField'] = sortedField.id
params['sortDirection'] = sortedField.desc ? 'desc' : 'asc'
}
tableActions.loadTable(params)
};
selectAllAction = () => {
const { tableActions } = this.props
tableActions.selectTableAllRows()
};
selectRowAction = (itemId) => {
const { tableActions } = this.props
tableActions.selectTableRow(itemId)
};
deleteRowAction = (itemId) => {
const { tableActions } = this.props
tableActions.confirmDelete([itemId])
};
nameClickAction = (item) => {
//implement in subclasses
};
onActivateButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleActive(tableState.selectedIds, true)
};
onPauseButtonClick = () => {
const { tableState, tableActions } = this.props
tableActions.toggleActive(tableState.selectedIds, false)
};
scheduleFormat (schedules) {
const { t } = this.props
if (!schedules) {
return ''
} else if (schedules.length === 0) {
return 'n/a'
} else if (schedules.length === 1) {
const schedule = schedules[0]
if (schedule.type === 'daily') {
const time = t(`notificationsTab.form.time.${schedule.time}`)
const days = t(`notificationsTab.form.days.${schedule.days}`)
return `${days}, ${time}`
} else if (schedule.type === 'weekly') {
const period = t(`notificationsTab.form.period.${schedule.period}`)
const day = t(`notificationsTab.form.day.${schedule.day}`)
const hour = padLeft(schedule.hour.toString(), 2)
const minute = padLeft(schedule.minute.toString(), 2)
return `${period} ${day}, ${hour}:${minute}`
} else if (schedule.type === 'monthly') {
const monthDay = addOrdinalSuffix(schedule.day)
const hour = padLeft(schedule.hour.toString(), 2)
const minute = padLeft(schedule.minute.toString(), 2)
return `${monthDay} of the month, ${hour}:${minute}`
}
return ''
} else {
return (
schedules.length + ' ' + this.props.t('notificationsTab.scheduledTimes')
)
}
}
getRestrictions (restrictions) {
if (!restrictions) return null
return (
<Restrictions restrictions={restrictions} restrictionsIds={['alerts', 'webFeeds']} />
)
}
getActionsPanel () {
//implement in subclasess
}
defineColumns () {
const { t, tableState } = this.props
return {
selectCheckbox: {
accessor: '',
sortable: false,
width: 45,
className: 'cw-center-cell',
headerClassName: 'cw-center-cell',
Header: () => {
return (
<CheckboxCell
checked={tableState.isAllSelected}
onChange={this.selectAllAction}
/>
)
},
Cell: ({ original }) => {
const isSelected = tableState.selectedIds.includes(original.id)
return (
<CheckboxCell
id={original.id}
checked={isSelected}
onChange={this.selectRowAction}
/>
)
}
},
name: {
Header: <SortableTh title="notificationsTab.name" />,
accessor: 'name',
Cell: ({ original }) => {
return (
<LinkCell item={original} onClick={this.nameClickAction}>
{original.name}
</LinkCell>
)
}
},
type: {
Header: <SortableTh title="notificationsTab.type" />,
accessor: (item) => t(`notificationsTab.${item.type}`),
width: 100
},
owner: {
Header: <SortableTh title="notificationsTab.owner" />,
accessor: (item) => item.owner.email,
width: 170
},
ScheduledTimes: {
sortable: false,
Header: t('notificationsTab.ScheduledTimes'),
accessor: (item) => this.scheduleFormat(item.automatic),
width: 170
},
sourcesCount: {
Header: <SortableTh title="notificationsTab.contents" />,
accessor: (item) =>
`${item.sourcesCount} ${t('notificationsTab.chartsFeeds')}`,
width: 170
},
delete: {
sortable: false,
Header: t('common:commonWords.Delete'),
accessor: '',
width: 65,
className: 'cw-center-cell',
headerClassName: 'cw-center-cell',
Cell: ({ original }) => {
return (
<DeleteButton id={original.id} onDelete={this.deleteRowAction} />
)
}
}
}
}
createTogglerColumn (
title,
toggleField,
enabledText,
disabledText,
togglerOnAction,
togglerOffAction
) {
const { t } = this.props
return {
Header: t(title),
sortable: false,
accessor: toggleField,
width: 180,
Cell: ({ original }) => {
return (
<Toggler
id={original.id}
turnOnAction={togglerOnAction}
turnOffAction={togglerOffAction}
state={original[toggleField]}
enabledText={enabledText}
disabledText={disabledText}
/>
)
}
}
}
getColumns () {
//implement in subclasses
//should return array of string
}
_fixColDefs (colDefs) {
for (let colId in colDefs) {
let colDef = colDefs[colId]
if (typeof colDef.accessor !== 'string' && !colDef.id) {
colDef.id = colId
}
}
}
noCard () {
// inherited class will return value
}
render () {
const {
tableActions,
tableState,
deleteSingleText,
deleteMultipleText
} = this.props
const cols = this.getColumns()
const colDefinitions = this.defineColumns()
const noCard = this.noCard()
this._fixColDefs(colDefinitions)
const columns = cols
.map((columnId) => {
const col = colDefinitions[columnId]
if (!col) {
console.error(this.displayName, ': cannot find column', columnId)
}
return col
})
.filter(Boolean)
return (
<div>
{this.getActionsPanel()}
<div>
<Table
columns={columns}
data={tableState.data}
totalCount={tableState.totalCount}
limit={tableState.limit}
page={tableState.page}
isLoading={tableState.isLoading}
onFetchData={this.fetchData}
onRowClick={this.onRowClick}
noCard={noCard}
/>
{tableState.isDeletePopupVisible && (
<DeletePopup
actions={tableActions}
idsToDelete={tableState.idsToDelete}
deleteSingleText={deleteSingleText}
deleteMultipleText={deleteMultipleText}
/>
)}
</div>
</div>
)
}
}
export default GenericTable
@@ -0,0 +1,41 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import TableSwitcherItem from './TableSwitcherItem'
import { ButtonGroup } from 'reactstrap'
export class TableSwitcher extends React.Component {
static propTypes = {
tables: PropTypes.array.isRequired,
tableVisible: PropTypes.string.isRequired,
subTab: PropTypes.string.isRequired,
switchTable: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
onTableClick = (item) => {
const { subTab, switchTable } = this.props
switchTable(subTab, item)
};
render () {
const { tables, tableVisible } = this.props
return (
<ButtonGroup className="bg-white">
{tables.map((table, i) => {
return (
<TableSwitcherItem
key={`tables-switcher__table-${i}`}
tableVisible={tableVisible}
table={table}
onClick={this.onTableClick}
/>
)
})}
</ButtonGroup>
)
}
}
export default translate(['tabsContent'], { wait: true })(TableSwitcher)
@@ -0,0 +1,35 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import { Button } from 'reactstrap'
export class TableSwitcherItem extends React.PureComponent {
static propTypes = {
table: PropTypes.string.isRequired,
tableVisible: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
onClick = () => {
const { onClick, table } = this.props
onClick(table)
};
render () {
const { t, table, tableVisible } = this.props
return (
<Button
outline
color="info"
onClick={this.onClick}
active={tableVisible === table}
>
{t(`tableSwitcher.${table}`)}
</Button>
)
}
}
export default translate(['tabsContent'], { wait: true })(TableSwitcherItem)