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,67 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import {
Button,
Modal,
ModalHeader,
ModalBody,
Label,
Input,
ModalFooter
} from 'reactstrap';
export class AddCategoryPopup extends React.Component {
state = {
folderName: ''
};
static propTypes = {
parentId: PropTypes.number.isRequired,
hideAddCategoryPopup: PropTypes.func.isRequired,
addCategory: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
onChangeName = (e) => {
const { value } = e.target; // need validation
this.setState({ folderName: value });
};
hidePopup = () => {
this.props.hideAddCategoryPopup();
};
onSubmit = () => {
const { folderName } = this.state;
this.props.addCategory(folderName, this.props.parentId);
this.props.hideAddCategoryPopup();
};
render() {
const { t } = this.props;
const { folderName } = this.state;
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static">
<ModalHeader toggle={this.hidePopup}>
{t('sidebarPopup.addFolderBtn')}
</ModalHeader>
<ModalBody>
<Label>{t('sidebarPopup.enterFolderName')}</Label>
<Input type="text" value={folderName} onChange={this.onChangeName} />
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('commonWords.Cancel')}
</Button>
<Button color="primary" onClick={this.onSubmit}>
{t('sidebarPopup.addFolderBtn')}
</Button>
</ModalFooter>
</Modal>
);
}
}
export default translate(['common'], { wait: true })(AddCategoryPopup);
@@ -0,0 +1,121 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import Select from 'react-select';
import {
Button,
Modal,
ModalHeader,
ModalBody,
FormGroup,
Label,
Input,
ModalFooter
} from 'reactstrap';
export class AddClippingsFeedPopup extends React.Component {
static propTypes = {
parentId: PropTypes.number.isRequired,
hidePopup: PropTypes.func.isRequired,
addClippingsFeed: PropTypes.func.isRequired,
addAlert: PropTypes.func.isRequired,
categories: PropTypes.array.isRequired,
t: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
parentId: props.parentId,
feedName: ''
};
}
onChangeName = (e) => {
const { value } = e.target;
this.setState({ feedName: value });
};
hidePopup = () => {
this.props.hidePopup();
};
onSubmit = () => {
const { parentId } = this.state;
const { addAlert, addClippingsFeed, hidePopup } = this.props;
const { feedName } = this.state;
if (feedName) {
addClippingsFeed(feedName, parentId);
hidePopup();
} else {
addAlert({
type: 'error',
transKey: 'feedNameEmpty'
});
}
};
flattenCategories = (categories, level = '') => {
return categories.reduce((result, category) => {
result.push({
label:
level +
this.props.t(`sidebar.${category.name}`, {
defaultValue: category.name
}),
value: category.id
});
if (category.childes && category.childes.length) {
return result.concat(
this.flattenCategories(category.childes, '- ' + level)
);
}
return result;
}, []);
};
onParentCategorySelect = (value) => {
this.setState({ parentId: value });
};
render() {
const { t, categories } = this.props;
const { parentId, feedName } = this.state;
const options = this.flattenCategories(categories);
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static">
<ModalHeader toggle={this.hidePopup}>
{t('sidebarPopup.addClippingsFeed')}
</ModalHeader>
<ModalBody>
<FormGroup>
<Label>{t('sidebarPopup.feedName')}</Label>
<Input type="text" value={feedName} onChange={this.onChangeName} />
</FormGroup>
<FormGroup>
<Label>{t('sidebarPopup.folder')}</Label>
<Select
onChange={this.onParentCategorySelect}
options={options}
value={parentId}
editable={false}
clearable={false}
simpleValue
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('commonWords.Cancel')}
</Button>
<Button color="primary" onClick={this.onSubmit}>
{t('sidebarPopup.addClippingsFeed')}
</Button>
</ModalFooter>
</Modal>
);
}
}
export default translate(['common'], { wait: true })(AddClippingsFeedPopup);
@@ -0,0 +1,58 @@
import React from 'react'
import PropTypes from 'prop-types'
import Category from './Category'
export class Categories extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired,
areCategoriesLoaded: PropTypes.bool.isRequired,
areFeedsFiltered: PropTypes.bool.isRequired,
categories: PropTypes.array.isRequired,
filteredCategories: PropTypes.array.isRequired
};
hideParentCategoryDrop = () => {}; //empty func for first level categories
render () {
const { areCategoriesLoaded, areFeedsFiltered, actions } = this.props
const {
showDeletePopup, showRenamePopup, showAddCategoryPopup,
showAddClippingsFeedPopup, getFeedResults,
moveCategory, moveFeed, toggleExportFeed,
toggleExportCategory, clipArticles
} = actions
const categories = areFeedsFiltered ? this.props.filteredCategories : this.props.categories
return (
<div className='sidebar-categories'>
{areCategoriesLoaded &&
categories.map((category, i) => {
return (
<Category
hideParentCategoryDrop={this.hideParentCategoryDrop} //set empty func
parentId={-1} //set empty parent category for first level categories
category={category}
categories={categories}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
showAddCategoryPopup={showAddCategoryPopup}
showAddClippingsFeedPopup={showAddClippingsFeedPopup}
getFeedResults={getFeedResults}
moveCategory={moveCategory}
moveFeed={moveFeed}
clipArticles={clipArticles}
key={'main-category' + i}
toggleExportFeed={toggleExportFeed}
toggleExportCategory={toggleExportCategory}
/>
)
})
}
</div>
)
}
}
export default Categories
@@ -0,0 +1,219 @@
import React from 'react';
import PropTypes from 'prop-types';
import { DropTarget, DragSource } from 'react-dnd';
import { compose } from 'redux';
import onClickOutside from 'react-onclickoutside';
import Feed from './Feed';
import CategoryHead from './CategoryHead';
import { TYPES } from '../../../redux/modules/appState/sidebar';
import cx from 'classnames';
const folderSource = {
beginDrag(props) {
return {
type: TYPES.FOLDER,
id: props.category.id,
category: props.category
};
},
canDrag(props) {
return props.category.type === 'directory';
}
};
const targetTypes = [TYPES.FEED, TYPES.FOLDER];
const categoryTarget = {
drop(props, monitor) {
if (monitor.didDrop()) return;
const { category, moveCategory, moveFeed } = props;
const item = monitor.getItem();
const draggedCategoryId = item.id;
const newCategoryId = category.id;
if (item.type === TYPES.FOLDER) {
moveCategory(item.category, newCategoryId);
} else if (item.type === TYPES.FEED) {
moveFeed(draggedCategoryId, newCategoryId);
}
},
canDrop(props, monitor) {
const categoryType = props.category.type;
return (
categoryType !== 'deleted_content' && categoryType !== 'shared_content'
);
}
};
export class CategoryClass extends React.Component {
static propTypes = {
parentId: PropTypes.number.isRequired,
category: PropTypes.object.isRequired,
showDeletePopup: PropTypes.func.isRequired,
showRenamePopup: PropTypes.func.isRequired,
showAddCategoryPopup: PropTypes.func.isRequired,
showAddClippingsFeedPopup: PropTypes.func.isRequired,
hideParentCategoryDrop: PropTypes.func.isRequired,
categories: PropTypes.array.isRequired,
connectDropTarget: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired,
getFeedResults: PropTypes.func.isRequired,
moveFeed: PropTypes.func.isRequired,
moveCategory: PropTypes.func.isRequired,
clipArticles: PropTypes.func.isRequired,
toggleExportFeed: PropTypes.func.isRequired,
toggleExportCategory: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
isCategoryActive: true, // sub menus
isCategoryDropActive: false // more options
};
}
// hide category dropdown if there was click outside
handleClickOutside = () => {
this.state.isCategoryDropActive && this.hideCategoryDropdown();
};
toggleCollapse = (e) => {
if (e.target === e.currentTarget) {
this.setState((prev) => ({
isCategoryActive: !prev.isCategoryActive
}));
}
};
toggleCategoryDropdown = (e) => {
e.preventDefault();
// this.props.hideParentCategoryDrop();
this.setState((prev) => ({
isCategoryDropActive: !prev.isCategoryDropActive
}));
};
hideCategoryDropdown = () => {
this.setState({
isCategoryDropActive: false
});
};
render() {
const {
category,
categories,
connectDropTarget,
connectDragSource,
hideParentCategoryDrop,
parentId,
showDeletePopup,
getFeedResults,
showRenamePopup,
showAddCategoryPopup,
moveCategory,
moveFeed,
showAddClippingsFeedPopup,
clipArticles,
toggleExportFeed,
toggleExportCategory
} = this.props;
const isFeeds = category.feeds.length > 0;
const isChildes = category.childes.length > 0;
const categoryType = category.type;
let categoryActiveClass = this.state.isCategoryActive
? ' active-category'
: '';
return connectDragSource(
connectDropTarget(
<li
className={'metismenu-item ' + categoryType + categoryActiveClass}
onClick={hideParentCategoryDrop}
>
<CategoryHead
toggleCollapse={this.toggleCollapse}
toggleCategoryDropdown={this.toggleCategoryDropdown}
isCategoryDropActive={this.state.isCategoryDropActive}
isCategoryActive={this.state.isCategoryActive}
hideDropDown={this.hideCategoryDropdown}
parentId={parentId}
category={category}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
showAddCategoryPopup={showAddCategoryPopup}
toggleExportCategory={toggleExportCategory}
showAddClippingsFeedPopup={showAddClippingsFeedPopup}
categories={categories}
/>
<ul
className={cx('metismenu-container', {
visible: this.state.isCategoryActive
})}
>
{isFeeds &&
category.feeds.map((feed, i) => {
return (
<Feed
key={'feed' + i}
feed={feed}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
categories={categories}
categoryId={category.id}
hideParentCategoryDrop={this.hideCategoryDropdown}
getFeedResults={getFeedResults}
clipArticles={clipArticles}
toggleExportFeed={toggleExportFeed}
/>
);
})}
{isChildes &&
category.childes.map((_category, i) => {
return (
<Category
key={'category' + i}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
showAddCategoryPopup={showAddCategoryPopup}
showAddClippingsFeedPopup={showAddClippingsFeedPopup}
parentId={category.id}
category={_category}
categories={categories}
hideParentCategoryDrop={this.hideCategoryDropdown}
getFeedResults={getFeedResults}
connectDropTarget={connectDropTarget}
connectDragSource={connectDragSource}
moveCategory={moveCategory}
moveFeed={moveFeed}
clipArticles={clipArticles}
toggleExportFeed={toggleExportFeed}
toggleExportCategory={toggleExportCategory}
/>
);
})}
</ul>
</li>
)
);
}
}
export const Category = compose(
DropTarget(targetTypes, categoryTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
itemType: monitor.getItemType()
})),
DragSource(TYPES.FOLDER, folderSource, (connect) => ({
connectDragSource: connect.dragSource()
}))
)(onClickOutside(CategoryClass));
export default Category;
@@ -0,0 +1,105 @@
import React from 'react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import SidebarDropdown from './SidebarDropdown';
import { translate } from 'react-i18next';
class CategoryHead extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
showDeletePopup: PropTypes.func.isRequired,
showRenamePopup: PropTypes.func.isRequired,
showAddCategoryPopup: PropTypes.func.isRequired,
showAddClippingsFeedPopup: PropTypes.func.isRequired,
toggleCollapse: PropTypes.func.isRequired,
toggleCategoryDropdown: PropTypes.func.isRequired,
toggleExportCategory: PropTypes.func.isRequired,
isCategoryDropActive: PropTypes.bool.isRequired,
isCategoryActive: PropTypes.bool.isRequired,
hideDropDown: PropTypes.func.isRequired,
parentId: PropTypes.number.isRequired,
category: PropTypes.object.isRequired,
categories: PropTypes.array.isRequired
};
getSidebarName(name) {
const catName = this.props.t(`sidebar.${name}`);
if (catName === `sidebar.${name}`) {
return name;
}
return catName;
}
render() {
const {
isCategoryActive,
isCategoryDropActive,
category,
categories,
showDeletePopup,
showRenamePopup,
showAddCategoryPopup,
showAddClippingsFeedPopup,
toggleExportCategory,
hideDropDown
} = this.props;
const isCategoryDeletedType = category.subType === 'deleted_content';
const categoryAttrId = 'sidebar-category' + category.id;
return (
<div
className="metismenu-link"
id={categoryAttrId}
onClick={this.props.toggleCollapse}
>
{/* <i className="sidebar-category__closed-icon" onClick={this.props.toggleCollapse}> </i>
<i className="sidebar-category__open-icon" onClick={this.props.toggleCollapse}> </i> */}
{isCategoryDeletedType ? (
<i className="metismenu-icon pe-7s-trash"></i>
) : (
<i className="metismenu-icon pe-7s-folder"></i>
)}
{this.getSidebarName(category.name)}
{!isCategoryDeletedType && (
<i
tabIndex="0"
className="metismenu-state-icon font-size-lg opacity-10 pe-7s-more mr-4"
onClick={this.props.toggleCategoryDropdown}
/>
)}
<i
className={cx(
'metismenu-state-icon pe-7s-angle-down pointer-events-none opacity-10',
{
'rotate-minus-90': isCategoryActive
}
)}
/>
{isCategoryDropActive && (
<SidebarDropdown
parentAttrId={categoryAttrId}
categories={categories}
itemId={category.id}
itemSubType={category.subType}
itemType={category.type}
itemName={category.name}
parentId={this.props.parentId}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
showAddCategoryPopup={showAddCategoryPopup}
showAddClippingsPopup={showAddClippingsFeedPopup}
hideDropDown={hideDropDown}
toggleExportCategory={toggleExportCategory}
/>
)}
</div>
);
}
}
export default translate(['common'], { wait: true })(CategoryHead);
@@ -0,0 +1,65 @@
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
export class DeletePopup extends React.Component {
static propTypes = {
itemToDelete: PropTypes.object.isRequired,
hideDeletePopup: PropTypes.func.isRequired,
deleteFeed: PropTypes.func.isRequired,
deleteCategory: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
hidePopup = () => {
this.props.hideDeletePopup();
};
onSubmit = () => {
const {
itemToDelete,
deleteCategory,
deleteFeed,
hideDeletePopup
} = this.props;
switch (this.props.itemToDelete.itemType) {
case 'feed':
deleteFeed(itemToDelete.itemId, itemToDelete.parentId);
break;
case 'directory':
deleteCategory(itemToDelete.itemId);
break;
}
hideDeletePopup();
};
render() {
const itemName = this.props.itemToDelete.itemName;
const itemType = this.props.itemToDelete.itemType;
const { t } = this.props;
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static">
<ModalHeader toggle={this.hidePopup}>
{t('commonWords.Confirm')}
</ModalHeader>
<ModalBody>
<p>
{t('messages.deleteMessage')} {itemType + ' "' + itemName + '"'}
</p>
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('commonWords.Cancel')}
</Button>
<Button color="danger" onClick={this.onSubmit}>
{t('commonWords.Delete')}
</Button>
</ModalFooter>
</Modal>
);
}
}
export default translate(['common'], { wait: true })(DeletePopup);
+162
View File
@@ -0,0 +1,162 @@
/** DRAG SOURCE **/
import React from 'react';
import PropTypes from 'prop-types';
import { compose } from 'redux';
import SidebarDropdown from './SidebarDropdown';
import { DragSource, DropTarget } from 'react-dnd';
import onClickOutside from 'react-onclickoutside';
import { TYPES } from '../../../redux/modules/appState/sidebar';
import { withRouter } from 'react-router-dom';
const feedSource = {
beginDrag(props) {
return {
type: TYPES.FEED,
id: props.feed.id,
feed: props.feed,
currentCategoryId: props.categoryId
};
}
};
/**
* Specifies which props to inject into component from Drag n Drop.
*/
function dragCollect(connect) {
return {
// Call this function inside render()
// to let React DnD handle the drag events:
connectDragSource: connect.dragSource()
};
}
/** DROP TARGET **/
const feedTarget = {
drop(props, monitor) {
if (monitor.didDrop()) return;
const { feed, clipArticles } = props;
clipArticles(feed.id);
},
canDrop(props, monitor) {
return props.feed.subType === 'clip_feed';
}
};
function dropCollect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget()
};
}
export class Feed extends React.Component {
static propTypes = {
feed: PropTypes.object.isRequired,
categoryId: PropTypes.number.isRequired,
categories: PropTypes.array.isRequired,
showDeletePopup: PropTypes.func.isRequired,
showRenamePopup: PropTypes.func.isRequired,
hideParentCategoryDrop: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
getFeedResults: PropTypes.func.isRequired,
clipArticles: PropTypes.func.isRequired,
toggleExportFeed: PropTypes.func.isRequired,
history: PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.state = {
isItemDropActive: false
};
}
//hide feed dropdown if there was click outside
handleClickOutside = () => {
this.state.isItemDropActive && this.hideDropDown();
};
hideDropDown = () => {
this.setState({
isItemDropActive: false
});
};
toggleItemDropdown = (e) => {
e.preventDefault();
this.setState({
isItemDropActive: !this.state.isItemDropActive
});
};
onFeedClick = (e) => {
const { history, getFeedResults, feed } = this.props;
e.preventDefault();
history.push('/app/search/search');
getFeedResults({ page: 1 }, feed.id);
window.scrollTo(0, 0);
};
render() {
const {
feed,
categoryId,
categories,
connectDragSource,
connectDropTarget,
showDeletePopup,
showRenamePopup,
toggleExportFeed
} = this.props;
const feedAttrId = 'sidebar-feed' + feed.id;
const dragAndDrop = compose(connectDragSource, connectDropTarget);
return dragAndDrop(
<li
id={feedAttrId}
onClick={this.props.hideParentCategoryDrop}
className="metismenu-item"
>
<a
href="#"
className={`metismenu-link feed-icon ${feed.class}`}
onClick={this.onFeedClick}
>
{feed.name}
</a>
<i
tabIndex="0"
className="metismenu-state-icon font-size-lg opacity-10 pe-7s-more"
onClick={this.toggleItemDropdown}
></i>
{this.state.isItemDropActive && (
<SidebarDropdown
parentAttrId={feedAttrId}
categories={categories}
itemId={feed.id}
itemType={feed.type}
itemSubType={feed.subType}
itemName={feed.name}
itemExported={feed.exported}
parentId={categoryId}
showDeletePopup={showDeletePopup}
showRenamePopup={showRenamePopup}
toggleExportFeed={toggleExportFeed}
hideDropDown={this.hideDropDown}
/>
)}
</li>
);
}
}
const applyDecorators = compose(
withRouter,
DragSource(TYPES.FEED, feedSource, dragCollect),
DropTarget([TYPES.CLIP_ARTICLE], feedTarget, dropCollect),
onClickOutside
);
export default applyDecorators(Feed);
@@ -0,0 +1,106 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export class Filter extends React.Component {
static propTypes = {
t: PropTypes.func.isRequired,
areFeedsFiltered: PropTypes.bool.isRequired,
categories: PropTypes.array.isRequired,
setFilteredCategories: PropTypes.func.isRequired,
clearFilteredCategories: PropTypes.func.isRequired
}
constructor (props) {
super(props)
this.state = {
sidebarAnimationDisabled: true,
activeSearch: false
}
}
activeSearchFunc = () => {
this.setState({ activeSearch: !this.state.activeSearch })
this.clearFilter()
}
filterCategoriesList = (
categories,
searchQuery,
setParentBranchMatchFromParent
) => {
// show category if there is feed
return categories.filter((category) => {
category.branchMatch = false
//function that sets parent branchMatch prop
function setParentBranchMatch (flag) {
category.branchMatch = flag
}
if (category.childes.length > 0) {
category.childes = this.filterCategoriesList(
category.childes,
searchQuery,
setParentBranchMatch
)
}
// filter feeds in category
category.feeds = category.feeds.filter((feed) => {
return feed.name.toLowerCase().indexOf(searchQuery) !== -1
})
// if this category is a child and it has matched feeds or its child have, then we set branchMatch prop of parent
if (
(category.feeds.length > 0 && setParentBranchMatchFromParent) ||
(category.branchMatch && setParentBranchMatchFromParent)
) {
setParentBranchMatchFromParent(true)
}
return category.branchMatch || category.feeds.length > 0
})
}
filterSidebarItems = (e) => {
const searchQuery = e.target.value.toLowerCase()
const categoriesCopy = this.props.categories.slice(0)
if (searchQuery.length) {
const filteredCat = this.filterCategoriesList(categoriesCopy, searchQuery)
this.props.setFilteredCategories(filteredCat)
} else {
this.props.clearFilteredCategories()
}
}
clearFilter = () => {
this.props.clearFilteredCategories()
}
render () {
return (
<div
className={classnames('search-wrapper mb-1', {
active: this.state.activeSearch
})}
>
<div className="input-holder">
<input
type="text"
className="search-input"
placeholder={this.props.t('common:sidebar.typeToSearch')}
onKeyUp={this.filterSidebarItems}
id="sidebar-search"
/>
<button onClick={this.activeSearchFunc} className="search-icon">
<span />
</button>
</div>
<button onClick={this.activeSearchFunc} className="close"></button>
</div>
)
}
}
export default Filter
@@ -0,0 +1,90 @@
import React from 'react'
import PropTypes from 'prop-types'
import { translate } from 'react-i18next'
import {
Button,
Input,
Label,
Modal,
ModalBody,
ModalFooter,
ModalHeader
} from 'reactstrap'
export class RenamePopup extends React.Component {
static propTypes = {
itemToRename: PropTypes.object.isRequired,
hideRenamePopup: PropTypes.func.isRequired,
renameFeed: PropTypes.func.isRequired,
renameCategory: PropTypes.func.isRequired,
addAlert: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
}
constructor(props) {
super(props)
this.state = {
itemName: props.itemToRename.itemName
}
}
hidePopup = () => {
this.props.hideRenamePopup()
}
onSubmit = () => {
const newName = this.state.itemName
const {
itemToRename,
renameFeed,
renameCategory,
hideRenamePopup
} = this.props
switch (this.props.itemToRename.itemType) {
case 'feed':
renameFeed(itemToRename.itemId, newName, itemToRename.parentId)
break
case 'directory':
renameCategory(itemToRename.itemId, newName, itemToRename.parentId)
break
}
hideRenamePopup()
}
onChangeName = (e) => {
const { value } = e.target // validation needed
this.setState({
itemName: value
})
}
render() {
const itemName = this.state.itemName
const { t } = this.props
return (
<Modal isOpen toggle={this.hidePopup} backdrop="static">
<ModalHeader toggle={this.hidePopup}>
{t('commonWords.Rename')}
</ModalHeader>
<ModalBody>
<Label>{t('sidebarPopup.enterNamelabel')}</Label>
<Input type="text" value={itemName} onChange={this.onChangeName} />
</ModalBody>
<ModalFooter>
<Button color="light" onClick={this.hidePopup}>
{t('commonWords.Cancel')}
</Button>
<Button color="primary" onClick={this.onSubmit}>
{t('commonWords.Rename')}
</Button>
</ModalFooter>
</Modal>
)
}
}
export default translate(['common'], { wait: true })(RenamePopup)
@@ -0,0 +1,155 @@
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import cx from 'classnames'
import Categories from './Categories'
import Filter from './Filter'
import DeletePopup from './DeletePopup'
import RenamePopup from './RenamePopup'
import AddCategoryPopup from './AddCategoryPopup'
import AddClippingsFeedPopup from './AddClippingsFeedPopup'
import LoadersAdvanced from '../../common/Loader/Loader'
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'
import PerfectScrollbar from 'react-perfect-scrollbar'
import HeaderLogo from '../AppHeader/HeaderLogo'
export class Sidebar extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired,
themeOptions: PropTypes.object.isRequired,
backgroundColor: PropTypes.string,
backgroundImage: PropTypes.any,
backgroundImageOpacity: PropTypes.any,
enableBackgroundImage: PropTypes.any,
enableMobileMenu: PropTypes.any,
enableSidebarShadow: PropTypes.any,
setEnableMobileMenu: PropTypes.func,
t: PropTypes.func,
sidebarState: PropTypes.object.isRequired
}
constructor (props) {
super(props)
this.state = {
sidebarAnimationDisabled: true,
activeSearch: false
}
}
toggleMobileSidebar = () => {
let { enableMobileMenu, setEnableMobileMenu } = this.props
setEnableMobileMenu(!enableMobileMenu)
}
componentDidMount = () => {
this.props.actions.getSidebarCategories()
}
activeSearchFunc = () => {
this.setState({ activeSearch: !this.state.activeSearch })
}
render () {
let {
backgroundColor,
enableBackgroundImage,
enableSidebarShadow,
backgroundImage,
backgroundImageOpacity
} = this.props.themeOptions
const { sidebarState, actions } = this.props
return (
<Fragment>
<div
className="sidebar-mobile-overlay"
onClick={this.toggleMobileSidebar}
/>
<CSSTransitionGroup
component="div"
className={cx('app-sidebar', backgroundColor, {
'sidebar-shadow': enableSidebarShadow
})}
transitionName="SidebarAnimation"
transitionAppear
transitionAppearTimeout={1500}
transitionEnter={false}
transitionLeave={false}
>
<HeaderLogo />
{!sidebarState.areCategoriesLoaded && <LoadersAdvanced />}
<PerfectScrollbar>
<div className="app-sidebar__inner mt-3">
<div className="vertical-nav-menu" data-tour="left-panel">
<div className="metismenu-container">
<Filter
t={this.props.t}
categories={sidebarState.categories}
areFeedsFiltered={sidebarState.areFeedsFiltered}
setFilteredCategories={actions.setFilteredCategories}
clearFilteredCategories={actions.clearFilteredCategories}
/>
<Categories
actions={actions}
areCategoriesLoaded={sidebarState.areCategoriesLoaded}
areFeedsFiltered={sidebarState.areFeedsFiltered}
categories={sidebarState.categories}
filteredCategories={sidebarState.filteredCategories}
/>
</div>
{sidebarState.popupVisible.delete && (
<DeletePopup
hideDeletePopup={actions.hideDeletePopup}
deleteFeed={actions.deleteFeed}
deleteCategory={actions.deleteCategory}
itemToDelete={sidebarState.popupItems.delete}
/>
)}
{sidebarState.popupVisible.rename && (
<RenamePopup
addAlert={actions.addAlert}
hideRenamePopup={actions.hideRenamePopup}
renameFeed={actions.renameFeed}
renameCategory={actions.renameCategory}
itemToRename={sidebarState.popupItems.rename}
/>
)}
{sidebarState.popupVisible.addCategory && (
<AddCategoryPopup
hideAddCategoryPopup={actions.hideAddCategoryPopup}
addCategory={actions.addCategory}
parentId={sidebarState.popupItems.addCategory.parentId}
/>
)}
{sidebarState.popupVisible.addClippingsFeed && (
<AddClippingsFeedPopup
parentId={sidebarState.popupItems.addClippingsFeed.parentId}
hidePopup={actions.hideAddClippingsFeedPopup}
addClippingsFeed={actions.addClippingsFeed}
addAlert={actions.addAlert}
categories={sidebarState.categories}
/>
)}
</div>
</div>
</PerfectScrollbar>
<div
className={cx('app-sidebar-bg', backgroundImageOpacity)}
style={{
backgroundImage: enableBackgroundImage
? 'url(' + backgroundImage + ')'
: null
}}
></div>
</CSSTransitionGroup>
</Fragment>
)
}
}
export default React.memo(Sidebar)
@@ -0,0 +1,154 @@
import React from 'react'
import PropTypes from 'prop-types'
import $ from 'jquery'
import { translate } from 'react-i18next'
export class SidebarDropdown extends React.Component {
static propTypes = {
itemName: PropTypes.string.isRequired,
itemSubType: PropTypes.string.isRequired,
itemType: PropTypes.string.isRequired,
itemId: PropTypes.number.isRequired,
itemExported: PropTypes.bool,
parentId: PropTypes.number.isRequired,
parentAttrId: PropTypes.string.isRequired,
showDeletePopup: PropTypes.func.isRequired,
showRenamePopup: PropTypes.func.isRequired,
showAddCategoryPopup: PropTypes.func,
showAddClippingsPopup: PropTypes.func,
toggleExportFeed: PropTypes.func,
toggleExportCategory: PropTypes.func,
t: PropTypes.func.isRequired,
hideDropDown: PropTypes.func.isRequired
};
constructor (props) {
super(props)
this.state = {
dropdownTopPos: 'auto',
dropdownBottomPos: 'auto',
dropdownOpacity: 0
}
}
componentDidMount = () => {
const topPos = $('#' + this.props.parentAttrId).offset().top - $(document).scrollTop()
const dropdownHeight = $('#sidebar-category-dropdown').height()
if ($(window).height() - topPos >= dropdownHeight) {
this.setState({
dropdownTopPos: topPos,
dropdownOpacity: 1
})
} else {
this.setState({
dropdownBottomPos: 5,
dropdownOpacity: 1
})
}
};
onExportToggle = () => {
const {itemId, toggleExportFeed, itemExported, hideDropDown} = this.props
toggleExportFeed(itemId, !itemExported)
hideDropDown()
};
onExportCategoryToggle = () => {
const {itemId, toggleExportCategory, itemExported, hideDropDown} = this.props
toggleExportCategory(itemId, !itemExported)
hideDropDown()
};
onDelete = () => {
this.props.showDeletePopup(this.props.itemId, this.props.itemType, this.props.itemName, this.props.parentId)
};
onRename = () => {
this.props.showRenamePopup(this.props.itemId, this.props.itemType, this.props.itemName, this.props.parentId)
};
onAddCategory = () => {
// set this item id as parent of new category
this.props.showAddCategoryPopup(this.props.itemId)
};
onAddClippingsFeedPopup = () => {
this.props.showAddClippingsPopup(this.props.itemId)
};
render () {
const { itemSubType, t, itemExported } = this.props
let dropdown
switch (itemSubType) {
case 'my_content':
dropdown = <ul id="sidebar-category-dropdown" className="sidebar-category__dropdown" style={{top: this.state.dropdownTopPos, bottom: this.state.dropdownBottomPos, opacity: this.state.dropdownOpacity}}>
<li><a href="#" onClick={this.onAddClippingsFeedPopup}>{t('sidebarDropdown.AddClippingsFeed')}</a></li>
<li><a href="#" onClick={this.onAddCategory}>{t('sidebarDropdown.AddFolder')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.DownloadSearchCriteria')}</a></li>
<li><a href="#">{t('sidebarDropdown.EditSearchTemplate')}</a></li>*/}
<li><a href="#" onClick={this.onExportCategoryToggle}>{t(itemExported ? 'sidebarDropdown.UnexportFeeds' : 'sidebarDropdown.ExportFeeds')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.ViewUserComments')}</a></li>*/}
</ul>
break
case 'shared_content':
dropdown = <ul id="sidebar-category-dropdown" className="sidebar-category__dropdown" style={{top: this.state.dropdownTopPos, bottom: this.state.dropdownBottomPos, opacity: this.state.dropdownOpacity}}>
<li><a href="#" onClick={this.onAddClippingsFeedPopup}>{t('sidebarDropdown.AddClippingsFeed')}</a></li>
<li><a href="#" onClick={this.onAddCategory}>{t('sidebarDropdown.AddFolder')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.DownloadSearchCriteria')}</a></li>*/}
<li><a href="#" onClick={this.onExportCategoryToggle}>{t(itemExported ? 'sidebarDropdown.UnexportFeeds' : 'sidebarDropdown.ExportFeeds')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.ViewUserComments')}</a></li>*/}
</ul>
break
case 'custom':
dropdown = <ul id="sidebar-category-dropdown" className="sidebar-category__dropdown" style={{top: this.state.dropdownTopPos, bottom: this.state.dropdownBottomPos, opacity: this.state.dropdownOpacity}}>
<li><a href="#" onClick={this.onAddClippingsFeedPopup}>{t('sidebarDropdown.AddClippingsFeed')}</a></li>
<li><a href="#" onClick={this.onAddCategory}>{t('sidebarDropdown.AddFolder')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.DownloadSearchCriteria')}</a></li>*/}
<li><a href="#" onClick={this.onExportCategoryToggle}>{t(itemExported ? 'sidebarDropdown.UnexportFeeds' : 'sidebarDropdown.ExportFeeds')}</a></li>
<li><a href="#" onClick={this.onRename}>{t('sidebarDropdown.RenameFolder')}</a></li>
{/*<li><a href="#">{t('sidebarDropdown.ViewUserComments')}</a></li>*/}
<li><a href="#" onClick={this.onDelete}>{t('sidebarDropdown.DeleteFolder')}</a></li>
</ul>
break
case 'query_feed':
dropdown = <ul id="sidebar-category-dropdown" className="sidebar-category__dropdown" style={{top: this.state.dropdownTopPos, bottom: this.state.dropdownBottomPos, opacity: this.state.dropdownOpacity}}>
{/*<li><a href="#">{t('sidebarDropdown.AddArticle')}</a></li>
<li><a href="#">{t('sidebarDropdown.AddToDashboard')}</a></li>
<li><a href="#">{t('sidebarDropdown.AnalyzeFeed')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadArticleData')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadFeedStatistics')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadSearchCriteria')}</a></li>*/}
<li><a href="#" onClick={this.onExportToggle}>{t(itemExported ? 'sidebarDropdown.UnexportFeed' : 'sidebarDropdown.ExportFeed')}</a></li>
<li><a href="#" onClick={this.onRename}>{t('sidebarDropdown.RenameFeed')}</a></li>
<li><a href="#" onClick={this.onDelete}>{t('sidebarDropdown.DeleteFeed')}</a></li>
</ul>
break
case 'clip_feed':
dropdown = <ul id="sidebar-category-dropdown" className="sidebar-category__dropdown" style={{top: this.state.dropdownTopPos, bottom: this.state.dropdownBottomPos, opacity: this.state.dropdownOpacity}}>
{/*<li><a href="#">{t('sidebarDropdown.AddArticle')}</a></li>
<li><a href="#">{t('sidebarDropdown.AddToDashboard')}</a></li>
<li><a href="#">{t('sidebarDropdown.AnalyzeFeed')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadArticleData')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadFeedStatistics')}</a></li>
<li><a href="#">{t('sidebarDropdown.DownloadSearchCriteria')}</a></li>*/}
<li><a href="#" onClick={this.onExportToggle}>{t(itemExported ? 'sidebarDropdown.UnexportFeed' : 'sidebarDropdown.ExportFeed')}</a></li>
<li><a href="#" onClick={this.onRename}>{t('sidebarDropdown.RenameFeed')}</a></li>
<li><a href="#" onClick={this.onDelete}>{t('sidebarDropdown.DeleteFeed')}</a></li>
</ul>
break
}
return (
dropdown
)
}
}
export default translate(['common'], { wait: true })(SidebarDropdown)