mirror of
https://github.com/wassname/talk.git
synced 2026-10-02 12:50:49 +08:00
Refactor admin
This commit is contained in:
1 parent
f32208b35b
commit
8ab7047cfa
91 files changed
+620
-689
No files matched your search
@@ -1,25 +1,20 @@
|
||||
import React from 'react';
|
||||
import {Router, Route, IndexRedirect, browserHistory} from 'react-router';
|
||||
|
||||
import Stories from 'containers/Stories/Stories';
|
||||
import Configure from 'containers/Configure/Configure';
|
||||
import LayoutContainer from 'containers/LayoutContainer';
|
||||
import InstallContainer from 'containers/Install/InstallContainer';
|
||||
import Configure from 'routes/Configure';
|
||||
import Dashboard from 'routes/Dashboard';
|
||||
import Install from 'routes/Install';
|
||||
import Stories from 'routes/Stories';
|
||||
import {CommunityLayout, Community} from 'routes/Community';
|
||||
import {ModerationLayout, Moderation} from 'routes/Moderation';
|
||||
|
||||
import CommunityLayout from 'containers/Community/CommunityLayout';
|
||||
import CommunityContainer from 'containers/Community/CommunityContainer';
|
||||
|
||||
import ModerationLayout from 'containers/ModerationQueue/ModerationLayout';
|
||||
import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
|
||||
|
||||
import Dashboard from 'containers/Dashboard/Dashboard';
|
||||
import Layout from 'containers/Layout';
|
||||
|
||||
const routes = (
|
||||
<div>
|
||||
<Route exact path="/admin/install" component={InstallContainer}/>
|
||||
<Route path='/admin' component={LayoutContainer}>
|
||||
<Route exact path="/admin/install" component={Install}/>
|
||||
<Route path='/admin' component={Layout}>
|
||||
<IndexRedirect to='/admin/moderate/all' />
|
||||
<Route path='community' component={CommunityContainer} />
|
||||
<Route path='configure' component={Configure} />
|
||||
<Route path='stories' component={Stories} />
|
||||
<Route path='dashboard' component={Dashboard} />
|
||||
@@ -27,11 +22,11 @@ const routes = (
|
||||
{/* Community Routes */}
|
||||
|
||||
<Route path='community' component={CommunityLayout}>
|
||||
<Route path='flagged' components={CommunityContainer}>
|
||||
<Route path=':id' components={CommunityContainer} />
|
||||
<Route path='flagged' components={Community}>
|
||||
<Route path=':id' components={Community} />
|
||||
</Route>
|
||||
<Route path='people' components={CommunityContainer}>
|
||||
<Route path=':id' components={CommunityContainer} />
|
||||
<Route path='people' components={Community}>
|
||||
<Route path=':id' components={Community} />
|
||||
</Route>
|
||||
<IndexRedirect to='flagged' />
|
||||
</Route>
|
||||
@@ -39,22 +34,22 @@ const routes = (
|
||||
{/* Moderation Routes */}
|
||||
|
||||
<Route path='moderate' component={ModerationLayout}>
|
||||
<Route path='all' components={ModerationContainer}>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path='all' components={Moderation}>
|
||||
<Route path=':id' components={Moderation} />
|
||||
</Route>
|
||||
<Route path='accepted' components={ModerationContainer}>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path='accepted' components={Moderation}>
|
||||
<Route path=':id' components={Moderation} />
|
||||
</Route>
|
||||
<Route path='premod' components={ModerationContainer}>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path='premod' components={Moderation}>
|
||||
<Route path=':id' components={Moderation} />
|
||||
</Route>
|
||||
<Route path='rejected' components={ModerationContainer}>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path='rejected' components={Moderation}>
|
||||
<Route path=':id' components={Moderation} />
|
||||
</Route>
|
||||
<Route path='flagged' components={ModerationContainer}>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path='flagged' components={Moderation}>
|
||||
<Route path=':id' components={Moderation} />
|
||||
</Route>
|
||||
<Route path=':id' components={ModerationContainer} />
|
||||
<Route path=':id' components={Moderation} />
|
||||
<IndexRedirect to='premod' />
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const singleView = () => ({type: actions.SINGLE_VIEW});
|
||||
|
||||
// Ban User Dialog
|
||||
export const showBanUserDialog = (user, commentId, commentStatus, showRejectedNote) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId, commentStatus, showRejectedNote});
|
||||
export const hideBanUserDialog = (showDialog) => ({type: actions.HIDE_BANUSER_DIALOG, showDialog});
|
||||
export const hideBanUserDialog = () => ({type: actions.HIDE_BANUSER_DIALOG});
|
||||
|
||||
// Suspend User Dialog
|
||||
export const showSuspendUserDialog = (userId, username, commentId, commentStatus) =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import styles from './ModerationList.css';
|
||||
import {Button} from 'coral-ui';
|
||||
import {menuActionsMap} from '../containers/ModerationQueue/helpers/moderationQueueActionsMap';
|
||||
import {menuActionsMap} from '../routes/Moderation/helpers/moderationQueueActionsMap';
|
||||
|
||||
const ActionButton = ({type = '', active, ...props}) => {
|
||||
const typeName = type.toLowerCase();
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
import styles from './Community.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations';
|
||||
import {setRole, setCommenterStatus} from '../../actions/community';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
class Table extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.onRoleChange = this.onRoleChange.bind(this);
|
||||
}
|
||||
|
||||
onRoleChange (id, role) {
|
||||
this.props.dispatch(setRole(id, role));
|
||||
}
|
||||
|
||||
onCommenterStatusChange (id, status) {
|
||||
this.props.dispatch(setCommenterStatus(id, status));
|
||||
}
|
||||
|
||||
render () {
|
||||
const {headers, commenters, onHeaderClickHandler} = this.props;
|
||||
|
||||
return (
|
||||
<table className={`mdl-data-table ${styles.dataTable}`}>
|
||||
<thead>
|
||||
<tr>
|
||||
{headers.map((header, i) =>(
|
||||
<th
|
||||
key={i}
|
||||
className="mdl-data-table__cell--non-numeric"
|
||||
onClick={() => onHeaderClickHandler({field: header.field})}>
|
||||
{header.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{commenters.map((row, i)=> (
|
||||
<tr key={i}>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.username}
|
||||
<span className={styles.email}>{row.profiles.map(({id}) => id)}</span>
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.created_at}
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
<SelectField label={'Select me'} value={row.status || ''}
|
||||
className={styles.selectField}
|
||||
label={lang.t('community.status')}
|
||||
onChange={(status) => this.onCommenterStatusChange(row.id, status)}>
|
||||
<Option value={'ACTIVE'}>{lang.t('community.active')}</Option>
|
||||
<Option value={'BANNED'}>{lang.t('community.banned')}</Option>
|
||||
</SelectField>
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
<SelectField label={'Select me'} value={row.roles[0] || ''}
|
||||
className={styles.selectField}
|
||||
label={lang.t('community.role')}
|
||||
onChange={(role) => this.onRoleChange(row.id, role)}>
|
||||
<Option value={''}>.</Option>
|
||||
<Option value={'STAFF'}>{lang.t('community.staff')}</Option>
|
||||
<Option value={'MODERATOR'}>{lang.t('community.moderator')}</Option>
|
||||
<Option value={'ADMIN'}>{lang.t('community.admin')}</Option>
|
||||
</SelectField>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect((state) => ({commenters: state.community.get('accounts')}))(Table);
|
||||
@@ -1,48 +0,0 @@
|
||||
import React from 'react';
|
||||
import styles from './Dashboard.css';
|
||||
import {compose} from 'react-apollo';
|
||||
import {connect} from 'react-redux';
|
||||
import {getMetrics} from 'coral-admin/src/graphql/queries';
|
||||
import FlagWidget from './FlagWidget';
|
||||
import ActivityWidget from './ActivityWidget';
|
||||
import CountdownTimer from 'coral-admin/src/components/CountdownTimer';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
|
||||
class Dashboard extends React.Component {
|
||||
|
||||
reloadData = () => {
|
||||
this.props.data.refetch();
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
if (this.props.data && this.props.data.loading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
const {data: {assetsByActivity, assetsByFlag}} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CountdownTimer handleTimeout={this.reloadData} />
|
||||
<div className={styles.Dashboard}>
|
||||
<FlagWidget assets={assetsByFlag} />
|
||||
<ActivityWidget assets={assetsByActivity} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
settings: state.settings.toJS(),
|
||||
moderation: state.moderation.toJS()
|
||||
};
|
||||
};
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps),
|
||||
getMetrics
|
||||
)(Dashboard);
|
||||
@@ -1,101 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import styles from './style.css';
|
||||
import {Wizard, WizardNav} from 'coral-ui';
|
||||
import Layout from 'coral-admin/src/components/ui/Layout';
|
||||
|
||||
import {
|
||||
goToStep,
|
||||
nextStep,
|
||||
submitUser,
|
||||
checkInstall,
|
||||
previousStep,
|
||||
finishInstall,
|
||||
submitSettings,
|
||||
updateUserFormData,
|
||||
updateSettingsFormData,
|
||||
updatePermittedDomains
|
||||
} from '../../actions/install';
|
||||
|
||||
import InitialStep from './components/Steps/InitialStep';
|
||||
import AddOrganizationName from './components/Steps/AddOrganizationName';
|
||||
import CreateYourAccount from './components/Steps/CreateYourAccount';
|
||||
import PermittedDomainsStep from './components/Steps/PermittedDomainsStep';
|
||||
import FinalStep from './components/Steps/FinalStep';
|
||||
|
||||
class InstallContainer extends Component {
|
||||
componentDidMount() {
|
||||
const {checkInstall} = this.props;
|
||||
checkInstall(() => {
|
||||
this.context.router.push('/admin');
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {install} = this.props;
|
||||
|
||||
return (
|
||||
<Layout restricted={true}>
|
||||
<div className={styles.Install}>
|
||||
{
|
||||
!install.alreadyInstalled ? (
|
||||
<div>
|
||||
<h2>Welcome to the Coral Project</h2>
|
||||
{ install.step !== 0 ? <WizardNav items={install.navItems} currentStep={install.step} icon='check'/> : null }
|
||||
<Wizard currentStep={install.step} {...this.props}>
|
||||
<InitialStep/>
|
||||
<AddOrganizationName/>
|
||||
<CreateYourAccount/>
|
||||
<PermittedDomainsStep/>
|
||||
<FinalStep/>
|
||||
</Wizard>
|
||||
</div>
|
||||
) : (
|
||||
<div>Talk is already installed</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InstallContainer.contextTypes = {
|
||||
router: React.PropTypes.object
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
install: state.install.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
nextStep: () => dispatch(nextStep()),
|
||||
goToStep: (step) => dispatch(goToStep(step)),
|
||||
previousStep: () => dispatch(previousStep()),
|
||||
finishInstall: () => dispatch(finishInstall()),
|
||||
checkInstall: (next) => dispatch(checkInstall(next)),
|
||||
handleDomainsChange: (value) => {
|
||||
dispatch(updatePermittedDomains(value));
|
||||
},
|
||||
handleSettingsChange: (e) => {
|
||||
const {name, value} = e.currentTarget;
|
||||
dispatch(updateSettingsFormData(name, value));
|
||||
},
|
||||
handleUserChange: (e) => {
|
||||
const {name, value} = e.currentTarget;
|
||||
dispatch(updateUserFormData(name, value));
|
||||
},
|
||||
handleSettingsSubmit: (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(submitSettings());
|
||||
},
|
||||
handleUserSubmit: (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(submitUser());
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InstallContainer);
|
||||
File renamed without changes.
@@ -1,187 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import styles from './Stories.css';
|
||||
import {connect} from 'react-redux';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import {fetchAssets, updateAssetState} from '../../actions/assets';
|
||||
import translations from '../../translations.json';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
import {Pager, Icon} from 'coral-ui';
|
||||
import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl';
|
||||
import EmptyCard from 'coral-admin/src/components/EmptyCard';
|
||||
|
||||
const limit = 25;
|
||||
|
||||
class Stories extends Component {
|
||||
|
||||
state = {
|
||||
search: '',
|
||||
sort: 'desc',
|
||||
filter: 'all',
|
||||
statusMenus: {},
|
||||
timer: null,
|
||||
page: 0
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchAssets(0, limit, '', this.state.sortBy);
|
||||
}
|
||||
|
||||
onSettingChange = (setting) => (e) => {
|
||||
let options = this.state;
|
||||
this.setState({[setting]: e.target.value});
|
||||
options[setting] = e.target.value;
|
||||
this.props.fetchAssets(0, limit, options.search, options.sort, options.filter);
|
||||
}
|
||||
|
||||
onSearchChange = (e) => {
|
||||
const search = e.target.value;
|
||||
this.setState((prevState) => {
|
||||
prevState.search = search;
|
||||
clearTimeout(prevState.timer);
|
||||
const fetchAssets = this.props.fetchAssets;
|
||||
prevState.timer = setTimeout(() => {
|
||||
fetchAssets(0, limit, search, this.state.sort, this.state.filter);
|
||||
}, 350);
|
||||
return prevState;
|
||||
});
|
||||
}
|
||||
|
||||
renderDate = (date) => {
|
||||
const d = new Date(date);
|
||||
return `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`;
|
||||
}
|
||||
|
||||
onStatusClick = (closeStream, id, statusMenuOpen) => () => {
|
||||
if (statusMenuOpen) {
|
||||
this.setState((prev) => {
|
||||
prev.statusMenus[id] = false;
|
||||
return prev;
|
||||
});
|
||||
this.props.updateAssetState(id, closeStream ? Date.now() : null)
|
||||
.then(() => {
|
||||
const {search, sort, filter, page} = this.state;
|
||||
this.props.fetchAssets(page, limit, search, sort, filter);
|
||||
});
|
||||
} else {
|
||||
this.setState((prev) => {
|
||||
prev.statusMenus[id] = true;
|
||||
return prev;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderTitle = (title, {id}) => <Link to={`/admin/moderate/${id}`}>{title}</Link>
|
||||
|
||||
renderStatus = (closedAt, {id}) => {
|
||||
const closed = closedAt && new Date(closedAt).getTime() < Date.now();
|
||||
const statusMenuOpen = this.state.statusMenus[id];
|
||||
return <div className={styles.statusMenu}>
|
||||
<div
|
||||
className={closed ? styles.statusMenuClosed : styles.statusMenuOpen}
|
||||
onClick={this.onStatusClick(closed, id, statusMenuOpen)}>
|
||||
{!statusMenuOpen && <Icon className={styles.statusMenuIcon} name='keyboard_arrow_down'/>}
|
||||
{closed ? lang.t('streams.closed') : lang.t('streams.open')}
|
||||
</div>
|
||||
{
|
||||
statusMenuOpen &&
|
||||
<div
|
||||
className={!closed ? styles.statusMenuClosed : styles.statusMenuOpen}
|
||||
onClick={this.onStatusClick(!closed, id, statusMenuOpen)}>
|
||||
{!closed ? lang.t('streams.closed') : lang.t('streams.open')}
|
||||
</div>
|
||||
}
|
||||
</div>;
|
||||
}
|
||||
|
||||
onPageClick = (page) => {
|
||||
this.setState({page});
|
||||
const {search, sort, filter} = this.state;
|
||||
this.props.fetchAssets((page - 1) * limit, limit, search, sort, filter);
|
||||
}
|
||||
|
||||
render () {
|
||||
const {search, sort, filter} = this.state;
|
||||
const {assets} = this.props;
|
||||
|
||||
const assetsIds = assets.ids.map((id) => assets.byId[id]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.leftColumn}>
|
||||
<div className={styles.searchBox}>
|
||||
<Icon name='search' className={styles.searchIcon}/>
|
||||
<input
|
||||
type='text'
|
||||
value={search}
|
||||
className={styles.searchBoxInput}
|
||||
onChange={this.onSearchChange}
|
||||
placeholder={lang.t('streams.search')}/>
|
||||
</div>
|
||||
<div className={styles.optionHeader}>{lang.t('streams.filter-streams')}</div>
|
||||
<div className={styles.optionDetail}>{lang.t('streams.stream-status')}</div>
|
||||
<RadioGroup
|
||||
name='status filter'
|
||||
value={filter}
|
||||
childContainer='div'
|
||||
onChange={this.onSettingChange('filter')}
|
||||
className={styles.radioGroup}
|
||||
>
|
||||
<Radio value='all'>{lang.t('streams.all')}</Radio>
|
||||
<Radio value='open'>{lang.t('streams.open')}</Radio>
|
||||
<Radio value='closed'>{lang.t('streams.closed')}</Radio>
|
||||
</RadioGroup>
|
||||
<div className={styles.optionHeader}>{lang.t('streams.sort-by')}</div>
|
||||
<RadioGroup
|
||||
name='sort by'
|
||||
value={sort}
|
||||
childContainer='div'
|
||||
onChange={this.onSettingChange('sort')}
|
||||
className={styles.radioGroup}
|
||||
>
|
||||
<Radio value='desc'>{lang.t('streams.newest')}</Radio>
|
||||
<Radio value='asc'>{lang.t('streams.oldest')}</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
{
|
||||
assetsIds.length
|
||||
? <div className={styles.mainContent}>
|
||||
<DataTable className={styles.streamsTable} rows={assetsIds} onClick={this.goToModeration}>
|
||||
<TableHeader name="title" cellFormatter={this.renderTitle}>{lang.t('streams.article')}</TableHeader>
|
||||
<TableHeader name="publication_date" cellFormatter={this.renderDate}>
|
||||
{lang.t('streams.pubdate')}
|
||||
</TableHeader>
|
||||
<TableHeader name="closedAt" cellFormatter={this.renderStatus} className={styles.status}>
|
||||
{lang.t('streams.status')}
|
||||
</TableHeader>
|
||||
</DataTable>
|
||||
<Pager
|
||||
totalPages={Math.ceil((assets.count || 0) / limit)}
|
||||
page={this.state.page}
|
||||
onNewPageHandler={this.onPageClick} />
|
||||
</div>
|
||||
: <EmptyCard>{lang.t('streams.empty_result')}</EmptyCard>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({assets}) => {
|
||||
return {
|
||||
assets: assets.toJS()
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
fetchAssets: (...args) => {
|
||||
dispatch(fetchAssets.apply(this, args));
|
||||
},
|
||||
updateAssetState: (...args) => dispatch(updateAssetState.apply(this, args))
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Stories);
|
||||
|
||||
const lang = new I18n(translations);
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import styles from '../Community.css';
|
||||
import styles from './Community.css';
|
||||
import BanUserButton from './BanUserButton';
|
||||
import {Button} from 'coral-ui';
|
||||
import {menuActionsMap} from '../../../containers/ModerationQueue/helpers/moderationQueueActionsMap';
|
||||
import {menuActionsMap} from '../../Moderation/helpers/moderationQueueActionsMap';
|
||||
|
||||
const ActionButton = ({type = '', user, ...props}) => {
|
||||
if (type === 'BAN') {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+20
-70
@@ -1,55 +1,26 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {compose} from 'react-apollo';
|
||||
|
||||
import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries';
|
||||
import {banUser, setUserStatus, rejectUsername} from 'coral-admin/src/graphql/mutations';
|
||||
|
||||
import {
|
||||
fetchAccounts,
|
||||
updateSorting,
|
||||
newPage,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog
|
||||
} from '../../actions/community';
|
||||
|
||||
import CommunityMenu from './components/CommunityMenu';
|
||||
import BanUserDialog from './components/BanUserDialog';
|
||||
import SuspendUserDialog from './components/SuspendUserDialog';
|
||||
|
||||
import CommunityMenu from './CommunityMenu';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import People from './People';
|
||||
import FlaggedAccounts from './FlaggedAccounts';
|
||||
|
||||
class CommunityContainer extends Component {
|
||||
export default class Community extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
state = {
|
||||
searchValue: '',
|
||||
timer: null
|
||||
};
|
||||
|
||||
this.state = {
|
||||
searchValue: '',
|
||||
timer: null
|
||||
};
|
||||
|
||||
this.onKeyDownHandler = this.onKeyDownHandler.bind(this);
|
||||
this.onSearchChange = this.onSearchChange.bind(this);
|
||||
this.onHeaderClickHandler = this.onHeaderClickHandler.bind(this);
|
||||
this.onNewPageHandler = this.onNewPageHandler.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.fetchAccounts({});
|
||||
}
|
||||
|
||||
onKeyDownHandler(e) {
|
||||
onKeyDownHandler = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
|
||||
onSearchChange(e) {
|
||||
onSearchChange = (e) => {
|
||||
const value = e.target.value;
|
||||
this.setState((prevState) => {
|
||||
prevState.searchValue = value;
|
||||
@@ -62,6 +33,16 @@ class CommunityContainer extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onHeaderClickHandler = (sort) => {
|
||||
this.props.updateSorting(sort);
|
||||
this.search();
|
||||
}
|
||||
|
||||
onNewPageHandler = (page) => {
|
||||
this.props.newPage(page);
|
||||
this.search({page});
|
||||
}
|
||||
|
||||
search(query = {}) {
|
||||
const {community} = this.props;
|
||||
|
||||
@@ -71,17 +52,6 @@ class CommunityContainer extends Component {
|
||||
asc: community.ascPeople,
|
||||
...query
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onHeaderClickHandler(sort) {
|
||||
this.props.dispatch(updateSorting(sort));
|
||||
this.search();
|
||||
}
|
||||
|
||||
onNewPageHandler(page) {
|
||||
this.props.dispatch(newPage(page));
|
||||
this.search({page});
|
||||
}
|
||||
|
||||
getTabContent(searchValue, props) {
|
||||
@@ -134,7 +104,6 @@ class CommunityContainer extends Component {
|
||||
|
||||
render() {
|
||||
const {searchValue} = this.state;
|
||||
|
||||
const tab = this.getTabContent(searchValue, this.props);
|
||||
|
||||
return (
|
||||
@@ -148,22 +117,3 @@ class CommunityContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
community: state.community.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
fetchAccounts: (query) => dispatch(fetchAccounts(query)),
|
||||
showBanUserDialog: (user) => dispatch(showBanUserDialog(user)),
|
||||
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
|
||||
showSuspendUserDialog: (user) => dispatch(showSuspendUserDialog(user)),
|
||||
hideSuspendUserDialog: () => dispatch(hideSuspendUserDialog())
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
modUserFlaggedQuery,
|
||||
banUser,
|
||||
setUserStatus,
|
||||
rejectUsername
|
||||
)(CommunityContainer);
|
||||
File renamed without changes.
File renamed without changes.
+1
-1
@@ -8,7 +8,7 @@ import styles from './Community.css';
|
||||
|
||||
import Loading from './Loading';
|
||||
import EmptyCard from 'coral-admin/src/components/EmptyCard';
|
||||
import User from './components/User';
|
||||
import User from './User';
|
||||
|
||||
const FlaggedAccounts = ({...props}) => {
|
||||
const {commenters, isFetching} = props;
|
||||
File renamed without changes.
+2
-2
@@ -3,9 +3,9 @@ import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from 'coral-admin/src/translations.json';
|
||||
|
||||
import styles from './Community.css';
|
||||
import Table from './Table';
|
||||
import Table from '../containers/Table';
|
||||
import {Pager, Icon} from 'coral-ui';
|
||||
import EmptyCard from '../../components/EmptyCard';
|
||||
import EmptyCard from '../../../components/EmptyCard';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
import styles from '../components/Community.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../../translations';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
export default ({headers, commenters, onHeaderClickHandler, onRoleChange, onCommenterStatusChange}) => (
|
||||
<table className={`mdl-data-table ${styles.dataTable}`}>
|
||||
<thead>
|
||||
<tr>
|
||||
{headers.map((header, i) =>(
|
||||
<th
|
||||
key={i}
|
||||
className="mdl-data-table__cell--non-numeric"
|
||||
onClick={() => onHeaderClickHandler({field: header.field})}>
|
||||
{header.title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{commenters.map((row, i)=> (
|
||||
<tr key={i}>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.username}
|
||||
<span className={styles.email}>{row.profiles.map(({id}) => id)}</span>
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.created_at}
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
<SelectField label={'Select me'} value={row.status || ''}
|
||||
className={styles.selectField}
|
||||
label={lang.t('community.status')}
|
||||
onChange={(status) => onCommenterStatusChange(row.id, status)}>
|
||||
<Option value={'ACTIVE'}>{lang.t('community.active')}</Option>
|
||||
<Option value={'BANNED'}>{lang.t('community.banned')}</Option>
|
||||
</SelectField>
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
<SelectField label={'Select me'} value={row.roles[0] || ''}
|
||||
className={styles.selectField}
|
||||
label={lang.t('community.role')}
|
||||
onChange={(role) => onRoleChange(row.id, role)}>
|
||||
<Option value={''}>.</Option>
|
||||
<Option value={'STAFF'}>{lang.t('community.staff')}</Option>
|
||||
<Option value={'MODERATOR'}>{lang.t('community.moderator')}</Option>
|
||||
<Option value={'ADMIN'}>{lang.t('community.admin')}</Option>
|
||||
</SelectField>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import styles from '../Community.css';
|
||||
import styles from './Community.css';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,55 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
|
||||
import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries';
|
||||
import {banUser, setUserStatus, rejectUsername} from 'coral-admin/src/graphql/mutations';
|
||||
|
||||
import {
|
||||
fetchAccounts,
|
||||
updateSorting,
|
||||
newPage,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog
|
||||
} from '../../../actions/community';
|
||||
|
||||
import Community from '../components/Community';
|
||||
|
||||
class CommunityContainer extends Component {
|
||||
|
||||
componentWillMount() {
|
||||
this.props.fetchAccounts({});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Community {...this.props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
community: state.community.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
fetchAccounts,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
updateSorting,
|
||||
newPage,
|
||||
}, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
modUserFlaggedQuery,
|
||||
banUser,
|
||||
setUserStatus,
|
||||
rejectUsername
|
||||
)(CommunityContainer);
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import {setRole, setCommenterStatus} from '../../../actions/community';
|
||||
import Table from '../components/Table';
|
||||
|
||||
class TableContainer extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render () {
|
||||
return <Table
|
||||
{...this.props}
|
||||
onRoleChange={this.props.setRole}
|
||||
onCommenterStatusChange={this.props.setCommenterStatus}
|
||||
commenters={this.props.commenters}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
commenters: state.community.get('accounts'),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
setCommenterStatus,
|
||||
setRole,
|
||||
}, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
)(TableContainer);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export {default as Community} from './containers/Community';
|
||||
export {default as CommunityLayout} from './components/CommunityLayout';
|
||||
File renamed without changes.
+12
-35
@@ -1,12 +1,4 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
fetchSettings,
|
||||
updateSettings,
|
||||
saveSettingsToServer,
|
||||
updateWordlist,
|
||||
updateDomainlist
|
||||
} from '../../actions/settings';
|
||||
|
||||
import {Button, List, Item, Card, Spinner} from 'coral-ui';
|
||||
import styles from './Configure.css';
|
||||
@@ -17,45 +9,38 @@ import ModerationSettings from './ModerationSettings';
|
||||
import TechSettings from './TechSettings';
|
||||
import {can} from 'coral-framework/services/perms';
|
||||
|
||||
class Configure extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
const lang = new I18n(translations);
|
||||
|
||||
this.state = {
|
||||
activeSection: 'stream',
|
||||
changed: false,
|
||||
errors: {}
|
||||
};
|
||||
export default class Configure extends Component {
|
||||
|
||||
this.changeSection = this.changeSection.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount = () => {
|
||||
this.props.dispatch(fetchSettings());
|
||||
}
|
||||
state = {
|
||||
activeSection: 'stream',
|
||||
changed: false,
|
||||
errors: {}
|
||||
};
|
||||
|
||||
saveSettings = () => {
|
||||
this.props.dispatch(saveSettingsToServer());
|
||||
this.props.saveSettingsToServer();
|
||||
this.setState({changed: false});
|
||||
}
|
||||
|
||||
changeSection(activeSection) {
|
||||
changeSection = (activeSection) => {
|
||||
this.setState({activeSection});
|
||||
}
|
||||
|
||||
onChangeWordlist = (listName, list) => {
|
||||
this.setState({changed: true});
|
||||
this.props.dispatch(updateWordlist(listName, list));
|
||||
this.props.updateWordlist(listName, list);
|
||||
}
|
||||
|
||||
onChangeDomainlist = (listName, list) => {
|
||||
this.setState({changed: true});
|
||||
this.props.dispatch(updateDomainlist(listName, list));
|
||||
this.props.updateDomainlist(listName, list);
|
||||
}
|
||||
|
||||
onSettingUpdate = (setting) => {
|
||||
this.setState({changed: true});
|
||||
this.props.dispatch(updateSettings(setting));
|
||||
this.props.updateSettings(setting);
|
||||
}
|
||||
|
||||
// Sets an arbitrary error string and a boolean state.
|
||||
@@ -176,11 +161,3 @@ class Configure extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
auth: state.auth.toJS(),
|
||||
settings: state.settings.toJS()
|
||||
});
|
||||
export default connect(mapStateToProps)(Configure);
|
||||
|
||||
const lang = new I18n(translations);
|
||||
+1
-1
@@ -4,7 +4,7 @@ import styles from './Configure.css';
|
||||
import TagsInput from 'react-tagsinput';
|
||||
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const Domainlist = ({domains, onChangeDomainlist}) => {
|
||||
+3
-3
@@ -1,9 +1,11 @@
|
||||
import React, {Component} from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
import styles from './Configure.css';
|
||||
import {Button, Card} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
class EmbedLink extends Component {
|
||||
|
||||
constructor (props) {
|
||||
@@ -64,5 +66,3 @@ class EmbedLink extends Component {
|
||||
}
|
||||
|
||||
export default EmbedLink;
|
||||
|
||||
const lang = new I18n(translations);
|
||||
+2
-2
@@ -4,7 +4,7 @@ import {Card} from 'coral-ui';
|
||||
import {Checkbox} from 'react-mdl';
|
||||
import Wordlist from './Wordlist';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const updateModeration = (updateSettings, mod) => () => {
|
||||
@@ -98,7 +98,7 @@ const ModerationSettings = ({settings, updateSettings, onChangeWordlist}) => {
|
||||
|
||||
{lang.t('configure.edit-comment-timeframe-text-post')}
|
||||
</p>
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
import styles from './Configure.css';
|
||||
import {Checkbox, Textfield} from 'react-mdl';
|
||||
import {Card, Icon, TextArea} from 'coral-ui';
|
||||
+1
-1
@@ -4,7 +4,7 @@ import Domainlist from './Domainlist';
|
||||
import EmbedLink from './EmbedLink';
|
||||
import styles from './Configure.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const updateCustomCssUrl = (updateSettings) => (event) => {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../../../translations.json';
|
||||
import TagsInput from 'react-tagsinput';
|
||||
import styles from './Configure.css';
|
||||
import {Card} from 'coral-ui';
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import {
|
||||
fetchSettings,
|
||||
updateSettings,
|
||||
saveSettingsToServer,
|
||||
updateWordlist,
|
||||
updateDomainlist
|
||||
} from '../../../actions/settings';
|
||||
|
||||
import Configure from '../components/Configure';
|
||||
|
||||
class ConfigureContainer extends Component {
|
||||
componentWillMount = () => {
|
||||
this.props.fetchSettings();
|
||||
}
|
||||
|
||||
render () {
|
||||
return <Configure {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
auth: state.auth.toJS(),
|
||||
settings: state.settings.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
fetchSettings,
|
||||
updateSettings,
|
||||
saveSettingsToServer,
|
||||
updateWordlist,
|
||||
updateDomainlist
|
||||
}, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
)(ConfigureContainer);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export {default} from './containers/Configure';
|
||||
File renamed without changes.
+1
-1
@@ -1,7 +1,7 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from 'coral-admin/src/translations';
|
||||
import styles from 'coral-admin/src/containers/Dashboard/Dashboard.css';
|
||||
import styles from './Dashboard.css';
|
||||
import {Icon} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
File renamed without changes.
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import FlagWidget from './FlagWidget';
|
||||
import ActivityWidget from './ActivityWidget';
|
||||
import CountdownTimer from './CountdownTimer';
|
||||
import styles from './Dashboard.css';
|
||||
|
||||
export default ({data: {assetsByActivity, assetsByFlag}, reloadData}) => (
|
||||
<div>
|
||||
<CountdownTimer handleTimeout={reloadData} />
|
||||
<div className={styles.Dashboard}>
|
||||
<FlagWidget assets={assetsByFlag} />
|
||||
<ActivityWidget assets={assetsByActivity} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import {compose} from 'react-apollo';
|
||||
import {connect} from 'react-redux';
|
||||
import {getMetrics} from 'coral-admin/src/graphql/queries';
|
||||
import Dashboard from '../components/Dashboard';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
|
||||
class DashboardContainer extends React.Component {
|
||||
|
||||
reloadData = () => {
|
||||
this.props.data.refetch();
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.props.data && this.props.data.loading) {
|
||||
return <Spinner />;
|
||||
}
|
||||
return <Dashboard {...this.props} reloadData={this.reloadData} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
settings: state.settings.toJS(),
|
||||
moderation: state.moderation.toJS()
|
||||
};
|
||||
};
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps),
|
||||
getMetrics
|
||||
)(DashboardContainer);
|
||||
@@ -0,0 +1 @@
|
||||
export {default} from './containers/Dashboard.js';
|
||||
@@ -0,0 +1,79 @@
|
||||
import React, {Component} from 'react';
|
||||
import styles from './style.css';
|
||||
import {Wizard, WizardNav} from 'coral-ui';
|
||||
import Layout from 'coral-admin/src/components/ui/Layout';
|
||||
|
||||
import InitialStep from './Steps/InitialStep';
|
||||
import AddOrganizationName from './Steps/AddOrganizationName';
|
||||
import CreateYourAccount from './Steps/CreateYourAccount';
|
||||
import PermittedDomainsStep from './Steps/PermittedDomainsStep';
|
||||
import FinalStep from './Steps/FinalStep';
|
||||
|
||||
export default class Install extends Component {
|
||||
handleDomainsChange = (value) => {
|
||||
this.props.updatePermittedDomains(value);
|
||||
};
|
||||
|
||||
handleSettingsChange = ({currentTarget: {name, value}}) => {
|
||||
this.props.updateSettingsFormData(name, value);
|
||||
};
|
||||
|
||||
handleUserChange = ({currentTarget: {name, value}}) => {
|
||||
this.props.updateUserFormData(name, value);
|
||||
};
|
||||
|
||||
handleSettingsSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.submitSettings();
|
||||
};
|
||||
|
||||
handleUserSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.submitUser();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {install} = this.props;
|
||||
|
||||
return (
|
||||
<Layout restricted={true}>
|
||||
<div className={styles.Install}>
|
||||
{
|
||||
!install.alreadyInstalled ? (
|
||||
<div>
|
||||
<h2>Welcome to the Coral Project</h2>
|
||||
{ install.step !== 0 ? <WizardNav items={install.navItems} currentStep={install.step} icon='check'/> : null }
|
||||
<Wizard
|
||||
currentStep={install.step}
|
||||
nextStep={this.props.nextStep}
|
||||
previousStep={this.props.previousStep}
|
||||
goToStep={this.props.goToStep}
|
||||
>
|
||||
<InitialStep/>
|
||||
<AddOrganizationName
|
||||
install={install}
|
||||
handleSettingsChange={this.handleSettingsChange}
|
||||
handleSettingsSubmit={this.handleSettingsSubmit}
|
||||
/>
|
||||
<CreateYourAccount
|
||||
install={install}
|
||||
handleUserChange={this.handleUserChange}
|
||||
handleUserSubmit={this.handleUserSubmit}
|
||||
/>
|
||||
<PermittedDomainsStep
|
||||
install={install}
|
||||
handleDomainsChange={this.handleDomainsChange}
|
||||
finishInstall={this.finishInstall}
|
||||
/>
|
||||
<FinalStep/>
|
||||
</Wizard>
|
||||
</div>
|
||||
) : (
|
||||
<div>Talk is already installed</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ import styles from './style.css';
|
||||
import {TextField, Button} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
const AddOrganizationName = (props) => {
|
||||
+1
-1
@@ -3,7 +3,7 @@ import styles from './style.css';
|
||||
import {TextField, Button, Spinner} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
const InitialStep = (props) => {
|
||||
+1
-1
@@ -4,7 +4,7 @@ import {Button} from 'coral-ui';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
const InitialStep = () => {
|
||||
+1
-1
@@ -3,7 +3,7 @@ import styles from './style.css';
|
||||
import {Button} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
const InitialStep = (props) => {
|
||||
File renamed without changes.
+1
-1
@@ -4,7 +4,7 @@ import {Button, Card} from 'coral-ui';
|
||||
import TagsInput from 'react-tagsinput';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
import translations from '../../translations.json';
|
||||
import translations from '../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
const PermittedDomainsStep = (props) => {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,57 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import Install from '../components/Install';
|
||||
|
||||
import {
|
||||
goToStep,
|
||||
nextStep,
|
||||
submitUser,
|
||||
checkInstall,
|
||||
previousStep,
|
||||
finishInstall,
|
||||
submitSettings,
|
||||
updateUserFormData,
|
||||
updateSettingsFormData,
|
||||
updatePermittedDomains
|
||||
} from '../../../actions/install';
|
||||
|
||||
class InstallContainer extends Component {
|
||||
componentDidMount() {
|
||||
const {checkInstall} = this.props;
|
||||
checkInstall(() => {
|
||||
this.context.router.push('/admin');
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Install {...this.props}/>;
|
||||
}
|
||||
}
|
||||
|
||||
InstallContainer.contextTypes = {
|
||||
router: React.PropTypes.object
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
install: state.install.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
goToStep,
|
||||
nextStep,
|
||||
submitUser,
|
||||
checkInstall,
|
||||
previousStep,
|
||||
finishInstall,
|
||||
submitSettings,
|
||||
updateUserFormData,
|
||||
updateSettingsFormData,
|
||||
updatePermittedDomains
|
||||
}, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
)(InstallContainer);
|
||||
@@ -0,0 +1 @@
|
||||
export {default} from './containers/InstallContainer.js';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+14
-80
@@ -1,44 +1,22 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import * as notification from 'coral-admin/src/services/notification';
|
||||
import key from 'keymaster';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import styles from './components/styles.css';
|
||||
import styles from './styles.css';
|
||||
import translations from 'coral-admin/src/translations';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
|
||||
import {modQueueQuery, getQueueCounts} from '../../graphql/queries';
|
||||
import {banUser, setCommentStatus, suspendUser} from '../../graphql/mutations';
|
||||
|
||||
import {fetchSettings} from 'actions/settings';
|
||||
import {updateAssets} from 'actions/assets';
|
||||
import {
|
||||
toggleModal,
|
||||
singleView,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
hideShortcutsNote,
|
||||
viewUserDetail,
|
||||
hideUserDetail
|
||||
} from 'actions/moderation';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
import BanUserDialog from './components/BanUserDialog';
|
||||
import SuspendUserDialog from './components/SuspendUserDialog';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import ModerationQueue from './ModerationQueue';
|
||||
import ModerationMenu from './components/ModerationMenu';
|
||||
import ModerationHeader from './components/ModerationHeader';
|
||||
import NotFoundAsset from './components/NotFoundAsset';
|
||||
import ModerationKeysModal from '../../components/ModerationKeysModal';
|
||||
import ModerationMenu from './ModerationMenu';
|
||||
import ModerationHeader from './ModerationHeader';
|
||||
import NotFoundAsset from './NotFoundAsset';
|
||||
import ModerationKeysModal from '../../../components/ModerationKeysModal';
|
||||
import UserDetail from './UserDetail';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
class ModerationContainer extends Component {
|
||||
export default class Moderation extends Component {
|
||||
state = {
|
||||
selectedIndex: 0,
|
||||
sort: 'REVERSE_CHRONOLOGICAL'
|
||||
@@ -47,7 +25,6 @@ class ModerationContainer extends Component {
|
||||
componentWillMount() {
|
||||
const {toggleModal, singleView} = this.props;
|
||||
|
||||
this.props.fetchSettings();
|
||||
key('s', () => singleView());
|
||||
key('shift+/', () => toggleModal(true));
|
||||
key('esc', () => toggleModal(false));
|
||||
@@ -57,6 +34,10 @@ class ModerationContainer extends Component {
|
||||
key('t', this.moderate(true));
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
this.toggleModal(false);
|
||||
}
|
||||
|
||||
moderate = (accept) => () => {
|
||||
const {acceptComment, rejectComment} = this.props;
|
||||
const {selectedIndex} = this.state;
|
||||
@@ -148,28 +129,13 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {updateAssets} = this.props;
|
||||
if(!isEqual(nextProps.data.assets, this.props.data.assets)) {
|
||||
updateAssets(nextProps.data.assets);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data, moderation, settings, assets, onClose, viewUserDetail, hideUserDetail, ...props} = this.props;
|
||||
const {data, moderation, settings, assets, viewUserDetail, hideUserDetail, ...props} = this.props;
|
||||
const providedAssetId = this.props.params.id;
|
||||
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
|
||||
|
||||
let asset;
|
||||
|
||||
if (!('premodCount' in data)) {
|
||||
return <div><Spinner/></div>;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
|
||||
if (providedAssetId) {
|
||||
asset = assets.find((asset) => asset.id === this.props.params.id);
|
||||
|
||||
@@ -253,7 +219,7 @@ class ModerationContainer extends Component {
|
||||
hideShortcutsNote={props.hideShortcutsNote}
|
||||
shortcutsNoteVisible={moderation.shortcutsNoteVisible}
|
||||
open={moderation.modalOpen}
|
||||
onClose={onClose}/>
|
||||
onClose={this.onClose}/>
|
||||
{moderation.userDetailId && (
|
||||
<UserDetail
|
||||
id={moderation.userDetailId}
|
||||
@@ -264,35 +230,3 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
moderation: state.moderation.toJS(),
|
||||
settings: state.settings.toJS(),
|
||||
auth: state.auth.toJS(),
|
||||
assets: state.assets.get('assets')
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose: () => dispatch(toggleModal(false)),
|
||||
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
|
||||
...bindActionCreators({
|
||||
toggleModal,
|
||||
singleView,
|
||||
updateAssets,
|
||||
fetchSettings,
|
||||
showBanUserDialog,
|
||||
hideShortcutsNote,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
viewUserDetail,
|
||||
hideUserDetail,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
setCommentStatus,
|
||||
getQueueCounts,
|
||||
banUser,
|
||||
suspendUser,
|
||||
modQueueQuery,
|
||||
)(ModerationContainer);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+5
-5
@@ -1,12 +1,12 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
|
||||
import Comment from './components/Comment';
|
||||
import styles from './components/styles.css';
|
||||
import EmptyCard from '../../components/EmptyCard';
|
||||
import {actionsMap} from './helpers/moderationQueueActionsMap';
|
||||
import Comment from './Comment';
|
||||
import styles from './styles.css';
|
||||
import EmptyCard from '../../../components/EmptyCard';
|
||||
import {actionsMap} from '../helpers/moderationQueueActionsMap';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from 'coral-admin/src/translations';
|
||||
import LoadMore from './components/LoadMore';
|
||||
import LoadMore from './LoadMore';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
class ModerationQueue extends React.Component {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3
-7
@@ -1,14 +1,13 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Button, Drawer} from 'coral-ui';
|
||||
import styles from './UserDetail.css';
|
||||
import {compose} from 'react-apollo';
|
||||
import {getUserDetail} from 'coral-admin/src/graphql/queries';
|
||||
import Slot from 'coral-framework/components/Slot';
|
||||
|
||||
class UserDetail extends React.Component {
|
||||
export default class UserDetail extends React.Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
hideUserDetail: PropTypes.func.isRequired
|
||||
hideUserDetail: PropTypes.func.isRequired,
|
||||
data: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
copyPermalink = () => {
|
||||
@@ -69,6 +68,3 @@ class UserDetail extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
getUserDetail
|
||||
)(UserDetail);
|
||||
File renamed without changes.
@@ -0,0 +1,83 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {modQueueQuery, getQueueCounts} from '../../../graphql/queries';
|
||||
import {banUser, setCommentStatus, suspendUser} from '../../../graphql/mutations';
|
||||
|
||||
import {fetchSettings} from 'actions/settings';
|
||||
import {updateAssets} from 'actions/assets';
|
||||
import {
|
||||
toggleModal,
|
||||
singleView,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
hideShortcutsNote,
|
||||
viewUserDetail,
|
||||
hideUserDetail
|
||||
} from 'actions/moderation';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
import Moderation from '../components/Moderation';
|
||||
|
||||
class ModerationContainer extends Component {
|
||||
componentWillMount() {
|
||||
this.props.fetchSettings();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {updateAssets} = this.props;
|
||||
if(!isEqual(nextProps.data.assets, this.props.data.assets)) {
|
||||
updateAssets(nextProps.data.assets);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data} = this.props;
|
||||
|
||||
if (!('premodCount' in data)) {
|
||||
return <div><Spinner/></div>;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
return <div>Error</div>;
|
||||
}
|
||||
return <Moderation {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
moderation: state.moderation.toJS(),
|
||||
settings: state.settings.toJS(),
|
||||
auth: state.auth.toJS(),
|
||||
assets: state.assets.get('assets')
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
...bindActionCreators({
|
||||
toggleModal,
|
||||
singleView,
|
||||
updateAssets,
|
||||
fetchSettings,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
hideShortcutsNote,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
viewUserDetail,
|
||||
hideUserDetail,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
setCommentStatus,
|
||||
getQueueCounts,
|
||||
banUser,
|
||||
suspendUser,
|
||||
modQueueQuery,
|
||||
)(ModerationContainer);
|
||||
@@ -0,0 +1,19 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {compose} from 'react-apollo';
|
||||
import {getUserDetail} from 'coral-admin/src/graphql/queries';
|
||||
import UserDetail from '../components/UserDetail';
|
||||
|
||||
class UserDetailContainer extends React.Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
hideUserDetail: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
return <UserDetail {...this.props}/>;
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
getUserDetail
|
||||
)(UserDetailContainer);
|
||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
||||
export {default as Moderation} from './containers/Moderation';
|
||||
export {default as ModerationLayout} from './components/ModerationLayout';
|
||||
File renamed without changes.
+3
-22
@@ -1,8 +1,6 @@
|
||||
import React, {Component} from 'react';
|
||||
import styles from './Stories.css';
|
||||
import {connect} from 'react-redux';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import {fetchAssets, updateAssetState} from 'coral-admin/src/actions/assets';
|
||||
import translations from 'coral-admin/src/translations.json';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
@@ -11,9 +9,11 @@ import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl';
|
||||
import EmptyCard from 'coral-admin/src/components/EmptyCard';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const limit = 25;
|
||||
|
||||
class Stories extends Component {
|
||||
export default class Stories extends Component {
|
||||
|
||||
state = {
|
||||
search: '',
|
||||
@@ -171,22 +171,3 @@ class Stories extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({assets}) => {
|
||||
return {
|
||||
assets: assets.toJS()
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
fetchAssets: (...args) => {
|
||||
dispatch(fetchAssets.apply(this, args));
|
||||
},
|
||||
updateAssetState: (...args) => dispatch(updateAssetState.apply(this, args))
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Stories);
|
||||
|
||||
const lang = new I18n(translations);
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import {fetchAssets, updateAssetState} from 'coral-admin/src/actions/assets';
|
||||
import Stories from '../components/Stories';
|
||||
|
||||
class StoriesContainer extends Component {
|
||||
render () {
|
||||
return <Stories {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
assets: state.assets.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
fetchAssets,
|
||||
updateAssetState,
|
||||
}, dispatch);
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
)(StoriesContainer);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export {default} from './containers/Stories.js';
|
||||
+4
-1
@@ -381,12 +381,15 @@ module.exports = class UsersService {
|
||||
* @param {Function} done callback after the operation is complete
|
||||
*/
|
||||
static addRoleToUser(id, role) {
|
||||
const roles = [];
|
||||
|
||||
// Check to see if the user role is in the allowable set of roles.
|
||||
if (USER_ROLES.indexOf(role) === -1) {
|
||||
if (role && USER_ROLES.indexOf(role) === -1) {
|
||||
|
||||
// User role is not supported! Error out here.
|
||||
return Promise.reject(new Error(`role ${role} is not supported`));
|
||||
} else {
|
||||
roles.push(role);
|
||||
}
|
||||
|
||||
return UserModel.update({id}, {$set: {roles: [role]}});
|
||||
|
||||
Reference in new issue
Block a user