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,118 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DateRangePicker } from 'react-dates'
import moment from 'moment'
import { getMomentObject } from '../../../../../../common/helper'
export class BetweenDatepickers extends React.Component {
state = {}
static propTypes = {
chosenSearchInterval: PropTypes.string.isRequired,
chosenStartDate: PropTypes.string.isRequired,
chosenEndDate: PropTypes.string.isRequired,
setSearchInterval: PropTypes.func.isRequired,
setSearchDate: PropTypes.func.isRequired,
setStartDate: PropTypes.func.isRequired,
minDate: PropTypes.object,
setEndDate: PropTypes.func.isRequired
}
swapDate = (startDate, endDate) => {
if (startDate.isAfter(endDate)) {
const temp = startDate
startDate = endDate
endDate = temp
}
return { startDate, endDate }
}
/*
setDates = (date, isStartDate) => {
const {
chosenStartDate,
chosenEndDate,
setStartDate,
setEndDate,
setSearchDate
} = this.props
const hasStartDate = !!chosenStartDate
const hasEndDate = !!chosenEndDate
let startDate = hasStartDate ? moment(chosenStartDate) : moment()
let endDate = hasEndDate ? moment(chosenEndDate) : moment()
startDate = isStartDate ? date : startDate
endDate = !isStartDate ? date : endDate
const swappedDate = this.swapDate(startDate, endDate)
startDate = swappedDate.startDate.format('YYYY-MM-DD')
endDate = swappedDate.endDate.format('YYYY-MM-DD')
setStartDate(startDate.format('YYYY-MM-DD'))
setEndDate(endDate.format('YYYY-MM-DD'))
const endDateLabel = hasEndDate ? endDate : 'now'
const startDateLabel = hasStartDate ? startDate : 'until'
let label = isStartDate
? `${startDate} - ${endDateLabel}`
: `${startDateLabel} - ${endDate}`
setSearchDate(label)
} */
setBetweenInterval = () => {
const { chosenSearchInterval, setSearchInterval } = this.props
if (chosenSearchInterval === 'between') return false
setSearchInterval('between')
}
handleDateChange = ({ startDate, endDate }) => {
const { setStartDate, setEndDate } = this.props
setStartDate(startDate ? startDate.format('YYYY-MM-DD') : null)
setEndDate(endDate ? endDate.format('YYYY-MM-DD') : null)
if (startDate && endDate) {
this.setBetweenInterval()
}
}
onFocusChange = (focus) => {
this.setState({ focusedInput: focus })
}
isOutsideRange = (date) => {
const today = moment()
return date.isAfter(today) || date.isBefore(this.props.minDate)
}
render() {
const { chosenStartDate, chosenEndDate } = this.props
const today = moment()
const startDate = getMomentObject(chosenStartDate)
const endDate = getMomentObject(chosenEndDate)
return (
<div className="ml-3">
<DateRangePicker
startDateId="startDate"
endDateId="endDate"
startDate={startDate}
endDate={endDate}
onDatesChange={this.handleDateChange}
focusedInput={this.state.focusedInput}
onFocusChange={this.onFocusChange}
displayFormat="MM/DD/YYYY"
startDatePlaceholderText="Start Date"
endDatePlaceholderText="End Date"
numberOfMonths={1}
maxDate={today}
// eslint-disable-next-line react/jsx-no-bind
isOutsideRange={this.isOutsideRange}
/>
</div>
)
}
}
export default BetweenDatepickers
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Col, CustomInput, FormGroup } from 'reactstrap';
export class DuplicatesTab extends React.Component {
static propTypes = {
includeDuplicates: PropTypes.bool.isRequired,
toggleIncludeDuplicates: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
render() {
const { t } = this.props;
return (
<Col sm={12}>
<FormGroup>
<CustomInput
className="checkbox-input-hidden"
type="checkbox"
id="duplicates-check"
checked={this.props.includeDuplicates}
onChange={this.props.toggleIncludeDuplicates}
label={t('searchTab.searchBySection.duplicates.includeDuplicates')}
/>
</FormGroup>
</Col>
);
}
}
export default translate(['tabsContent'], { wait: true })(DuplicatesTab);
@@ -0,0 +1,53 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Col, FormGroup, Input, Label } from 'reactstrap';
export class EmphasisTab extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
include: PropTypes.string.isRequired,
exclude: PropTypes.string.isRequired,
setHeadlineIncluded: PropTypes.func.isRequired,
setHeadlineExcluded: PropTypes.func.isRequired
};
setHeadInclude = (e) => {
const headline = e.target.value;
this.props.setHeadlineIncluded(headline);
};
setHeadExclude = (e) => {
const headline = e.target.value;
this.props.setHeadlineExcluded(headline);
};
render() {
const { t, include, exclude } = this.props;
return (
<Fragment>
<Col sm="6">
<FormGroup>
<Label>
{t('searchTab.searchBySection.emphasis.headlineLabel')}{' '}
{t('searchTab.searchBySection.emphasis.include')}
</Label>
<Input type="text" value={include} onChange={this.setHeadInclude} />
</FormGroup>
</Col>
<Col sm="6">
<FormGroup>
<Label>
{t('searchTab.searchBySection.emphasis.headlineLabel')}{' '}
{t('searchTab.searchBySection.emphasis.exclude')}
</Label>
<Input type="text" value={exclude} onChange={this.setHeadExclude} />
</FormGroup>
</Col>
</Fragment>
);
}
}
export default translate(['tabsContent'], { wait: true })(EmphasisTab);
@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Col, CustomInput, FormGroup } from 'reactstrap';
function ExtrasTab({ t, hasImages, toggleHasImages }) {
return (
<Col sm={12}>
<FormGroup>
<CustomInput
id="has-images-check"
type="checkbox"
className="d-flex"
checked={hasImages}
label={t('searchTab.searchBySection.extras.hasImages')}
onChange={toggleHasImages}
/>
</FormGroup>
</Col>
);
}
ExtrasTab.propTypes = {
hasImages: PropTypes.bool.isRequired,
toggleHasImages: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
export default translate(['tabsContent'], { wait: true })(ExtrasTab);
@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Col, CustomInput } from 'reactstrap';
export class LangsTab extends React.Component {
static propTypes = {
chosenLanguages: PropTypes.array.isRequired,
searchLanguages: PropTypes.array.isRequired,
toggleLang: PropTypes.func.isRequired,
toggleAllLangs: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
toggleLangs = ({ target: { id, checked } }) => {
this.props.toggleLang(id, checked);
};
toggleAllLangs = (e) => {
this.props.toggleAllLangs(e.target.checked);
};
render() {
const { t } = this.props;
const { searchLanguages, chosenLanguages } = this.props;
return (
<Col sm={12} className="search-by-lang">
<CustomInput
id="article-check-all"
type="checkbox"
label={t('common:language.all')}
checked={searchLanguages.length === chosenLanguages.length}
onChange={this.toggleAllLangs}
/>
{searchLanguages.map((lang) => (
<CustomInput
key={lang}
id={lang}
type="checkbox"
checked={chosenLanguages.indexOf(lang) !== -1}
label={t('common:language.' + lang)}
onChange={this.toggleLangs}
/>
))}
</Col>
);
}
}
export default translate(['tabsContent'], { wait: true })(LangsTab);
@@ -0,0 +1,61 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DragSource } from 'react-dnd'
const Types = {
LOC: 'location'
}
const locationSource = {
beginDrag (props) {
// Return the data describing the dragged item
return { oldDropTargetType: props.dropTargetType }
},
endDrag (props, monitor, component) {
// When dropped on a compatible target, do something
if (monitor.getDropResult() !== null) {
const locFrom = props.dropTargetType
const locTo = monitor.getDropResult().newDropTargetType
const locationType = props.locationType
const location = props.location
props.moveLocation(locFrom, locTo, locationType, location)
}
}
}
/**
* Specifies which props to inject into your component.
*/
function collectDragSource (connect) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource()
}
}
export class LocationsTabList extends React.Component {
static propTypes = {
location: PropTypes.object.isRequired,
dropTargetType: PropTypes.string.isRequired,
moveLocation: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired
};
render () {
const { connectDragSource } = this.props
const { location } = this.props
return connectDragSource(
<li className="list-group-item cursor-move p-2">
<span className="drag-handle" />
{location.name}
</li>
)
}
}
export default DragSource(Types.LOC, locationSource, collectDragSource)(LocationsTabList)
@@ -0,0 +1,111 @@
/* eslint-disable react/jsx-no-bind */
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import LocationsTabList from './LocationsTabList';
import { Button, Col, Row } from 'reactstrap';
export class LocationsTab extends React.Component {
static propTypes = {
locations: PropTypes.array.isRequired,
locationsToInclude: PropTypes.array.isRequired,
locationsToExclude: PropTypes.array.isRequired,
chosenLocationsType: PropTypes.string.isRequired,
changeLocationsType: PropTypes.func.isRequired,
moveLocation: PropTypes.func.isRequired,
clearLocations: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
dropdownOpen: false,
dropDownValue: 'country'
};
}
onClearLocations = () => {
this.props.clearLocations();
this.props.changeLocationsType('country');
this.setState({ dropDownValue: 'country' });
};
selectLocation = (value) => {
this.props.changeLocationsType(value);
this.setState({ dropDownValue: value });
};
render() {
const {
locations,
chosenLocationsType,
locationsToInclude,
locationsToExclude
} = this.props;
const { t } = this.props;
const locationsMainList = locations.filter((loc) => {
return loc.type === chosenLocationsType;
});
const includeList = locationsToInclude.filter((loc) => {
return loc.type === chosenLocationsType;
});
const excludeList = locationsToExclude.filter((loc) => {
return loc.type === chosenLocationsType;
});
const { dropDownValue } = this.state;
return (
<Col sm={12}>
<Button
outline
active={dropDownValue === 'country'}
color="secondary"
className="mr-2 mb-3"
onClick={() => this.selectLocation('country')}
>
{t('searchTab.searchBySection.locations.countriesSelect')}
</Button>
<Button
outline
active={dropDownValue === 'state'}
color="secondary"
className="mb-3"
onClick={() => this.selectLocation('state')}
>
{t('searchTab.searchBySection.locations.statesSelect')}
</Button>
<Row className="draggable">
<Col md={4}>
<LocationsTabList
locations={locationsMainList}
chosenLocationsType={chosenLocationsType}
dropTargetType="locations"
moveLocation={this.props.moveLocation}
/>
</Col>
<Col md={4}>
<LocationsTabList
locations={includeList}
chosenLocationsType={chosenLocationsType}
dropTargetType="locationsToInclude"
moveLocation={this.props.moveLocation}
/>
</Col>
<Col md={4}>
<LocationsTabList
locations={excludeList}
chosenLocationsType={chosenLocationsType}
dropTargetType="locationsToExclude"
moveLocation={this.props.moveLocation}
/>
</Col>
</Row>
</Col>
);
}
}
export default translate(['tabsContent'], { wait: true })(LocationsTab);
@@ -0,0 +1,94 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { DropTarget } from 'react-dnd';
import flow from 'lodash/flow';
import LocationItem from './LocationItem';
import {
ListGroup
} from 'reactstrap';
const targetTypes = ['location'];
const locationListTarget = {
drop(props, monitor, component) {
if (monitor.didDrop()) {
//check whether some nested
// target already handled drop
return;
}
return { newDropTargetType: props.dropTargetType };
},
canDrop(props, monitor) {
return props.dropTargetType !== monitor.getItem().oldDropTargetType;
}
};
function collectDropTarget(connect, monitor) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDropTarget: connect.dropTarget(),
// You can ask the monitor about the current drag state:
itemType: monitor.getItemType()
};
}
export class LocationsTabList extends React.Component {
static propTypes = {
locations: PropTypes.array.isRequired,
chosenLocationsType: PropTypes.string.isRequired,
dropTargetType: PropTypes.string.isRequired,
moveLocation: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired
};
render() {
const { locations, chosenLocationsType, dropTargetType } = this.props;
const { t } = this.props;
const { connectDropTarget } = this.props;
locations.forEach((location) => {
location.name = t('common:' + location.type + '.' + location.code);
});
const sortedLocations = locations.sort((a, b) => {
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
if (nameA < nameB) {
//sort string ascending
return -1;
}
if (nameA > nameB) {
return 1;
}
return 0;
});
return connectDropTarget(
<div className="scroll-area-md border b-radius-5">
<p className="text-muted border-bottom p-2">{t('searchTab.searchBySection.locations.' + dropTargetType)}</p>
<ListGroup className="p-2">
{sortedLocations.map((location, i) => {
return (
<LocationItem
key={'location-' + i}
location={location}
dropTargetType={dropTargetType}
locationType={chosenLocationsType}
moveLocation={this.props.moveLocation}
/>
);
})}
</ListGroup>
</div>
);
}
}
export default flow(
DropTarget(targetTypes, locationListTarget, collectDropTarget),
translate(['tabsContent'], { wait: true })
)(LocationsTabList);
@@ -0,0 +1,163 @@
import React from 'react';
import PropTypes from 'prop-types';
import SearchByTabs from './SearchByTabs';
import EmphasisTab from './EmphasisTab';
import LangsTab from './LangsTab';
import LocationsTab from './LocationsTab';
import SourcesTab from './SourcesTab';
import SourceListsTab from './SourceListsTab';
import DuplicatesTab from './DuplicatesTab';
import ExtrasTab from './ExtrasTab';
import { translate } from 'react-i18next';
import { Button, Container, Row } from 'reactstrap';
export class SearchBy extends React.Component {
static propTypes = {
userSubscriptionDate: PropTypes.string.isRequired,
userSubscription: PropTypes.string.isRequired,
searchByFiltersState: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
animationDisabled: true,
arrowPosition: true
};
}
onToggleSearchBy = () => {
this.props.actions.toggleSearchBy();
};
render() {
const { t } = this.props;
const { searchByFiltersState, actions } = this.props;
const visibleClass = searchByFiltersState.isSearchByVisible
? ' visible'
: ' closed';
return (
<div
className={'search-by-container mb-3 mb-md-0' + visibleClass}
data-tour="advanced-search"
>
<div className="search-by">
<SearchByTabs
searchByTabs={searchByFiltersState.searchByTabs}
chooseSearchByTab={actions.chooseSearchByTab}
chosenSearchByTab={searchByFiltersState.chosenSearchByTab}
/>
<Container fluid>
<Row className="mb-3" data-tour="advanced-search-content">
{searchByFiltersState.chosenSearchByTab === 'emphasis' && (
<EmphasisTab
include={searchByFiltersState.headlineIncluded}
exclude={searchByFiltersState.headlineExcluded}
setHeadlineIncluded={actions.setHeadlineIncluded}
setHeadlineExcluded={actions.setHeadlineExcluded}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'languages' && (
<LangsTab
searchLanguages={searchByFiltersState.searchLanguages}
chosenLanguages={searchByFiltersState.chosenLanguages}
toggleLang={actions.toggleLang}
toggleAllLangs={actions.toggleAllLangs}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'locations' && (
<LocationsTab
locations={searchByFiltersState.locations}
chosenLocationsType={searchByFiltersState.chosenLocationsType}
locationsToInclude={searchByFiltersState.locationsToInclude}
locationsToExclude={searchByFiltersState.locationsToExclude}
changeLocationsType={actions.changeLocationsType}
moveLocation={actions.moveLocation}
clearLocations={actions.clearLocations}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'sources' && (
<SourcesTab
chosenMediaTypes={searchByFiltersState.chosenMediaTypes}
chosenLanguages={searchByFiltersState.chosenLanguages}
searchBySources={searchByFiltersState.searchBySources}
searchBySourcesType={searchByFiltersState.searchBySourcesType}
selectedSearchBySources={
searchByFiltersState.selectedSearchBySources
}
searchBySourcesQuery={
searchByFiltersState.searchBySourcesQuery
}
setSearchBySourcesQuery={actions.setSearchBySourcesQuery}
getSearchBySources={actions.getSearchBySources}
addSelectedSearchBySource={actions.addSelectedSearchBySource}
removeSelectedSearchBySource={
actions.removeSelectedSearchBySource
}
clearSearchBySources={actions.clearSearchBySources}
includeExcludeSearchBySources={
actions.includeExcludeSearchBySources
}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'sourceLists' && (
<SourceListsTab
searchBySourceLists={
searchByFiltersState.searchBySourceListsAvailable
}
searchBySourceListsToInclude={
searchByFiltersState.searchBySourceListsToInclude
}
searchBySourceListsToExclude={
searchByFiltersState.searchBySourceListsToExclude
}
getSourceLists={actions.getSearchBySourceLists}
moveSourceList={actions.moveSourceList}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'duplicates' && (
<DuplicatesTab
includeDuplicates={searchByFiltersState.includeDuplicates}
toggleIncludeDuplicates={actions.toggleIncludeDuplicates}
/>
)}
{searchByFiltersState.chosenSearchByTab === 'extras' && (
<ExtrasTab
hasImages={searchByFiltersState.hasImages}
toggleHasImages={actions.toggleHasImages}
/>
)}
</Row>
</Container>
</div>
<hr className="mt-0 mb-2" />
<Button
outline
size="sm"
className="font-size-xs"
color="secondary"
onClick={this.onToggleSearchBy}
>
{t('searchTab.searchBySection.searchByBtn')}
{searchByFiltersState.isSearchByVisible ? (
<i className="lnr-chevron-up btn-icon-wrapper"></i>
) : (
<i className="lnr-chevron-down btn-icon-wrapper"></i>
)}
</Button>
</div>
);
}
}
export default translate(['tabsContent'], { wait: true })(SearchBy);
@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Nav, NavLink, NavItem } from 'reactstrap';
import { translate } from 'react-i18next';
export class SearchByTabs extends React.Component {
static propTypes = {
searchByTabs: PropTypes.array.isRequired,
chosenSearchByTab: PropTypes.string.isRequired,
chooseSearchByTab: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
chooseSearchByTab = (newTab) => () => {
this.props.chooseSearchByTab(newTab);
};
render() {
const { searchByTabs } = this.props;
const { t } = this.props;
return (
<Nav tabs className="font-size-xs">
{searchByTabs.map((tab, i) => (
<NavItem key={tab}>
<NavLink
className="d-block"
active={tab === this.props.chosenSearchByTab}
onClick={this.chooseSearchByTab(tab)}
>
{t('searchTab.searchBySection.' + tab + '.title')}
</NavLink>
</NavItem>
))}
</Nav>
);
}
}
export default translate(['tabsContent'], { wait: true })(SearchByTabs);
@@ -0,0 +1,25 @@
import React from 'react'
import PropTypes from 'prop-types'
export class SourceIcon extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired
};
acceptedTypes = ['blogs', 'clippings', 'forums', 'mixed', 'news', 'prints', 'socials', 'user-added', 'user-comments', 'videos'];
render () {
const { type } = this.props
if (!this.acceptedTypes.includes(type)) {
return null
}
return (
<img src={require('../../../../../../images/feed-type-' + type + '.png')} className="source-icon" />
)
}
}
export default SourceIcon
@@ -0,0 +1,56 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import SourceListsTabList from './SourceListsTabList';
import { Col } from 'reactstrap';
export class SourceListsTab extends React.Component {
static propTypes = {
searchBySourceLists: PropTypes.array.isRequired,
searchBySourceListsToInclude: PropTypes.array.isRequired,
searchBySourceListsToExclude: PropTypes.array.isRequired,
getSourceLists: PropTypes.func.isRequired,
moveSourceList: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
componentWillMount = () => {
this.props.getSourceLists({ page: 1, limit: 25 });
};
render() {
const {
searchBySourceLists,
searchBySourceListsToInclude,
searchBySourceListsToExclude
} = this.props;
return (
<Fragment>
<Col md={4}>
<SourceListsTabList
sourceLists={searchBySourceLists}
dropTargetType="searchBySourceListsAvailable"
moveSourceList={this.props.moveSourceList}
/>
</Col>
<Col md={4}>
<SourceListsTabList
sourceLists={searchBySourceListsToInclude}
dropTargetType="searchBySourceListsToInclude"
moveSourceList={this.props.moveSourceList}
/>
</Col>
<Col md={4}>
<SourceListsTabList
sourceLists={searchBySourceListsToExclude}
dropTargetType="searchBySourceListsToExclude"
moveSourceList={this.props.moveSourceList}
/>
</Col>
</Fragment>
);
}
}
export default translate(['tabsContent'], { wait: true })(SourceListsTab);
@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DragSource } from 'react-dnd';
const Types = {
SOURCE_LIST: 'sourceList'
};
const sourceListSource = {
beginDrag(props) {
// Return the data describing the dragged item
return { oldDropTargetType: props.dropTargetType };
},
endDrag(props, monitor, component) {
// When dropped on a compatible target, do something
if (monitor.getDropResult() !== null) {
const from = props.dropTargetType;
const to = monitor.getDropResult().newDropTargetType;
const sourceList = props.sourceList;
props.moveSourceList(from, to, sourceList);
}
}
};
/**
* Specifies which props to inject into your component.
*/
function collectDragSource(connect) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource()
};
}
export class SourceListsTabItem extends React.Component {
static propTypes = {
sourceList: PropTypes.func.isRequired,
dropTargetType: PropTypes.string.isRequired,
moveSourceList: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired
};
render() {
const { connectDragSource } = this.props;
const { sourceList } = this.props;
return connectDragSource(
<li className="list-group-item cursor-move p-2">
<span className="drag-handle" />
{sourceList.name}
</li>
);
}
}
export default DragSource(
Types.SOURCE_LIST,
sourceListSource,
collectDragSource
)(SourceListsTabItem);
@@ -0,0 +1,75 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { DropTarget } from 'react-dnd';
import flow from 'lodash/flow';
import SourceListsTabItem from './SourceListsTabItem';
import { ListGroup } from 'reactstrap';
const targetTypes = ['sourceList'];
const sourceListTarget = {
drop(props, monitor, component) {
if (monitor.didDrop()) {
//check whether some nested
// target already handled drop
return;
}
return { newDropTargetType: props.dropTargetType };
},
canDrop(props, monitor) {
return props.dropTargetType !== monitor.getItem().oldDropTargetType;
}
};
function collectDropTarget(connect, monitor) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDropTarget: connect.dropTarget(),
// You can ask the monitor about the current drag state:
itemType: monitor.getItemType()
};
}
export class SourceListsTabList extends React.Component {
static propTypes = {
sourceLists: PropTypes.array.isRequired,
dropTargetType: PropTypes.string.isRequired,
moveSourceList: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired
};
render() {
const { sourceLists, dropTargetType } = this.props;
const { t } = this.props;
const { connectDropTarget } = this.props;
return connectDropTarget(
<div className="draggable scroll-area-md border b-radius-5">
<p className="text-muted border-bottom p-2">
{t('searchTab.searchBySection.sourceLists.' + dropTargetType)}
</p>
<ListGroup className="p-2">
{sourceLists.map((sourceList, i) => {
return (
<SourceListsTabItem
key={'sourceList-' + i}
sourceList={sourceList}
dropTargetType={dropTargetType}
moveSourceList={this.props.moveSourceList}
/>
);
})}
</ListGroup>
</div>
);
}
}
export default flow(
DropTarget(targetTypes, sourceListTarget, collectDropTarget),
translate(['tabsContent'], { wait: true })
)(SourceListsTabList);
@@ -0,0 +1,67 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import SourcesTabAvailSources from './SourcesTabAvailSources';
import SourcesTabSelectedSources from './SourcesTabSelectedSources';
import { Col } from 'reactstrap';
export class SourcesTab extends React.Component {
static propTypes = {
chosenMediaTypes: PropTypes.array.isRequired,
chosenLanguages: PropTypes.array.isRequired,
searchBySources: PropTypes.array.isRequired,
selectedSearchBySources: PropTypes.array.isRequired,
searchBySourcesType: PropTypes.string.isRequired,
searchBySourcesQuery: PropTypes.string.isRequired,
setSearchBySourcesQuery: PropTypes.func.isRequired,
getSearchBySources: PropTypes.func.isRequired,
addSelectedSearchBySource: PropTypes.func.isRequired,
removeSelectedSearchBySource: PropTypes.func.isRequired,
clearSearchBySources: PropTypes.func.isRequired,
includeExcludeSearchBySources: PropTypes.func.isRequired
};
render() {
const {
searchBySourcesQuery,
setSearchBySourcesQuery,
chosenMediaTypes,
chosenLanguages,
searchBySources,
getSearchBySources,
addSelectedSearchBySource,
searchBySourcesType,
clearSearchBySources,
selectedSearchBySources,
removeSelectedSearchBySource,
includeExcludeSearchBySources
} = this.props;
return (
<Fragment>
<Col sm={8}>
<SourcesTabAvailSources
searchBySourcesQuery={searchBySourcesQuery}
selectedSources={selectedSearchBySources}
setSearchBySourcesQuery={setSearchBySourcesQuery}
chosenMediaTypes={chosenMediaTypes}
chosenLanguages={chosenLanguages}
availSources={searchBySources}
getSearchBySources={getSearchBySources}
addSelectedSearchBySource={addSelectedSearchBySource}
/>
</Col>
<Col sm={4}>
<SourcesTabSelectedSources
searchBySourcesType={searchBySourcesType}
clearSearchBySources={clearSearchBySources}
selectedSources={selectedSearchBySources}
removeSelectedSearchBySource={removeSelectedSearchBySource}
includeExcludeSearchBySources={includeExcludeSearchBySources}
/>
</Col>
</Fragment>
);
}
}
export default SourcesTab;
@@ -0,0 +1,160 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
// import SourceIcon from './SourceIcon';
import { Button, Input, InputGroup, InputGroupAddon, Table } from 'reactstrap';
import { capitalize } from 'lodash';
import { getTitle } from '../../../../../../common/helper';
import cx from 'classnames';
import { domainNames } from '../SearchSubTab';
export class SourcesTabAvailSources extends React.Component {
static propTypes = {
chosenMediaTypes: PropTypes.array.isRequired,
chosenLanguages: PropTypes.array.isRequired,
availSources: PropTypes.array.isRequired,
selectedSources: PropTypes.array.isRequired,
searchBySourcesQuery: PropTypes.string.isRequired,
setSearchBySourcesQuery: PropTypes.func.isRequired,
getSearchBySources: PropTypes.func.isRequired,
addSelectedSearchBySource: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
componentDidMount = () => {
this.searchSources();
};
searchSources = () => {
const {
chosenLanguages,
chosenMediaTypes,
getSearchBySources,
searchBySourcesQuery
} = this.props;
const query = searchBySourcesQuery;
const dataToSend = {};
dataToSend.page = 1;
dataToSend.limit = 100;
dataToSend.query = query;
dataToSend.filters = {};
const source = []
const domain = []
chosenMediaTypes.map((v) => {
if (domainNames.includes(v)) {
domain.push(`${v}.com`);
} else {
source.push(v);
}
})
dataToSend.filters.publisher = { source, domain };
dataToSend.filters.language = chosenLanguages;
getSearchBySources(dataToSend);
};
chooseSource = (e) => {
const dataset = e.currentTarget.dataset;
const sourceTitle = dataset.sourceTitle;
const sourceType = dataset.sourceType;
const sourceId = dataset.sourceId;
this.props.addSelectedSearchBySource({
title: sourceTitle,
type: sourceType,
id: sourceId
});
};
onChangeSearchInput = (e) => {
const val = e.target.value;
this.props.setSearchBySourcesQuery(val);
};
onEnterSearchInput = (e) => {
if (e.keyCode === 13) this.searchSources();
};
render() {
const { availSources, selectedSources } = this.props;
const { t } = this.props;
return (
<Fragment>
<InputGroup className="mb-3">
<Input
type="text"
id="search-by-sources-input"
value={this.props.searchBySourcesQuery}
onChange={this.onChangeSearchInput}
onKeyUp={this.onEnterSearchInput}
/>
<InputGroupAddon addonType="append">
<Button
color="primary"
className="btn-icon btn-icon-only"
onClick={this.searchSources}
>
<i className="lnr-magnifier btn-icon-wrapper"></i>
</Button>
</InputGroupAddon>
</InputGroup>
<p className="text-muted">
{t('searchTab.searchBySection.sources.availSources')}
</p>
<div className="source-table-wrap border">
<Table striped bordered className="mb-0">
<thead>
<tr>
<th>{t('searchTab.searchBySection.sources.source')}</th>
<th>{t('searchTab.searchBySection.sources.siteType')}</th>
<th>{t('searchTab.searchBySection.sources.mediatype')}</th>
<th>{t('searchTab.searchBySection.sources.lang')}</th>
</tr>
</thead>
<tbody>
{availSources.length > 0 ? (
availSources.map((source, i) => {
return (
<tr
title="Click to select"
className={cx('clickable', {
active:
selectedSources &&
selectedSources.find((v) => v.id === source.id)
})}
data-source-title={source.title}
data-source-type={source.type}
data-source-id={source.id}
onClick={this.chooseSource}
key={i}
>
{/* <td>
<SourceIcon type={source.type} />
</td> */}
<td>{getTitle(source.title)}</td>
<td title={source.url}>
{capitalize(source.siteType) || '-'}
</td>
<td>{capitalize(source.type) || '-'}</td>
<td>{t(`common:language.${source.lang}`)}</td>
</tr>
);
})
) : (
<tr className="p-4 text-center text-black-50">
<td colSpan="4">{t('common:messages.noRows')}</td>
</tr>
)}
</tbody>
</Table>
</div>
</Fragment>
);
}
}
export default translate(['tabsContent'], { wait: true })(
SourcesTabAvailSources
);
@@ -0,0 +1,122 @@
/* eslint-disable react/jsx-no-bind */
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Button, CustomInput, Table } from 'reactstrap';
import { IoIosCloseCircleOutline } from 'react-icons/io';
import { capitalize } from 'lodash';
import { getTitle } from '../../../../../../common/helper';
export class SourcesTabSelectedSources extends React.Component {
static propTypes = {
searchBySourcesType: PropTypes.string.isRequired,
selectedSources: PropTypes.array.isRequired,
removeSelectedSearchBySource: PropTypes.func.isRequired,
clearSearchBySources: PropTypes.func.isRequired,
includeExcludeSearchBySources: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
removeSource = (sourceId) => {
this.props.removeSelectedSearchBySource(sourceId);
};
includeExclide = (type) => {
this.props.includeExcludeSearchBySources(type);
};
render() {
const { selectedSources } = this.props;
const { t } = this.props;
return (
<Fragment>
<div className="d-flex flex-wrap my-3">
<CustomInput
type="radio"
name="include-exclude-source"
className="d-flex mr-2"
checked={this.props.searchBySourcesType === 'include'}
id="include-sources-radio"
onChange={() => this.includeExclide('include')}
label={t('searchTab.searchBySection.sources.includeText')}
/>
<CustomInput
type="radio"
name="include-exclude-source"
checked={this.props.searchBySourcesType === 'exclude'}
className="d-flex mr-2"
id="exclude-sources-radio"
onChange={() => this.includeExclide('exclude')}
label={t('searchTab.searchBySection.sources.excludeText')}
/>
</div>
<p className="text-muted">
{t('searchTab.searchBySection.sources.selectedSources')}
</p>
<div className="source-table-wrap border">
<Table striped className="mb-0">
<thead>
<tr>
<th>{t('searchTab.searchBySection.sources.source')}</th>
<th>{t('searchTab.searchBySection.sources.mediatype')}</th>
<th style={{ width: '50px' }}></th>
</tr>
</thead>
<tbody>
{selectedSources.length > 0 ? (
selectedSources.map((source, i) => {
return (
<tr key={i}>
{/* <td>
<SourceIcon type={source.type} />
</td> */}
<td>{getTitle(source.title)}</td>
<td>{capitalize(source.type) || '-'}</td>
<td>
<button
title="Remove"
type="button"
className="btn p-0"
onClick={() => this.removeSource(source.id)}
>
<IoIosCloseCircleOutline
size={22}
className="text-danger ml-2"
/>
</button>
</td>
</tr>
);
})
) : (
<tr className="p-4 text-center text-black-50">
<td colSpan="3">
{t('common:messages.noRows')} <br />
{t('searchTab.searchBySection.sources.selectSource')}
</td>
</tr>
)}
</tbody>
</Table>
</div>
{selectedSources.length > 0 && (
<Button
size="sm"
className="d-block ml-auto mt-2 mb-2"
onClick={this.props.clearSearchBySources}
>
{t('searchTab.clearBtn')}
</Button>
)}
</Fragment>
);
}
}
export default translate(['tabsContent'], { wait: true })(
SourcesTabSelectedSources
);