mirror of
https://github.com/wassname/talk.git
synced 2026-07-01 13:29:26 +08:00
Merge branch 'master' into registration-fix
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
.dialog {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 280px;
|
||||
top: 10px;
|
||||
}
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 500px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 184px;
|
||||
padding: 20px;
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h2 {
|
||||
color: black;
|
||||
font-size: 1.76em;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header h1, .separator h1{
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
h3 {
|
||||
color: black;
|
||||
font-size: 1.4em;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.formField {
|
||||
@@ -143,5 +151,14 @@ input.error{
|
||||
}
|
||||
|
||||
.cancel {
|
||||
margin: 10px 0;
|
||||
margin-right: 10px;
|
||||
width: 47%;
|
||||
}
|
||||
|
||||
.ban {
|
||||
width: 47%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
@@ -19,23 +19,23 @@ const BanUserDialog = ({open, handleClose, onClickBanUser, user = {}}) => (
|
||||
<span className={styles.close} onClick={handleClose}>×</span>
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h3>
|
||||
<h2>
|
||||
{lang.t('bandialog.ban_user')}
|
||||
</h3>
|
||||
</h2>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h4>
|
||||
<h3>
|
||||
{lang.t('bandialog.are_you_sure', user.userName)}
|
||||
</h4>
|
||||
</h3>
|
||||
<i>
|
||||
{lang.t('bandialog.note')}
|
||||
</i>
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={() => handleClose()} full>
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={() => handleClose()} raised>
|
||||
{lang.t('bandialog.cancel')}
|
||||
</Button>
|
||||
<Button cStyle="black" onClick={() => onClickBanUser(user.userId, user.commentId)} full>
|
||||
<Button cStyle="black" className={styles.ban} onClick={() => onClickBanUser(user.userId, user.commentId)} raised>
|
||||
{lang.t('bandialog.yes_ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,8 @@ import styles from './CommentList.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
|
||||
import {Icon} from 'react-mdl';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import {FabButton, Button} from 'coral-ui';
|
||||
import {FabButton, Button, Icon} from 'coral-ui';
|
||||
|
||||
const linkify = new Linkify();
|
||||
|
||||
@@ -20,22 +19,19 @@ export default props => {
|
||||
const links = linkify.getMatches(comment.body);
|
||||
|
||||
return (
|
||||
<li tabIndex={props.index} className={`${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
|
||||
<li tabIndex={props.index} className={`mdl-card mdl-shadow--2dp ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
|
||||
<div className={styles.itemHeader}>
|
||||
<div className={styles.author}>
|
||||
<i className={`material-icons ${styles.avatar}`}>person</i>
|
||||
<span>{author.displayName || lang.t('comment.anon')}</span>
|
||||
<span className={styles.created}>{timeago().format(comment.createdAt || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
|
||||
{comment.flagged ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.sideActions}>
|
||||
{links ?
|
||||
<span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
|
||||
<div className={`actions ${styles.actions}`}>
|
||||
{props.modActions.map((action, i) => getActionButton(action, i, props))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{authorStatus === 'banned' ?
|
||||
<span className={styles.banned}><Icon name='error_outline'/> {lang.t('comment.banned_user')}</span> : null}
|
||||
</div>
|
||||
@@ -65,15 +61,19 @@ const getActionButton = (action, i, props) => {
|
||||
}
|
||||
if (action === 'ban') {
|
||||
return (
|
||||
<Button
|
||||
className='ban'
|
||||
cStyle='black'
|
||||
disabled={banned ? 'disabled' : ''}
|
||||
onClick={() => props.onClickShowBanDialog(author.id, author.displayName, comment.id)}
|
||||
key={i}
|
||||
>
|
||||
{lang.t('comment.ban_user')}
|
||||
</Button>
|
||||
<div className={styles.ban}>
|
||||
<Button
|
||||
className={`ban ${styles.banButton}`}
|
||||
cStyle='darkGrey'
|
||||
disabled={banned ? 'disabled' : ''}
|
||||
onClick={() => props.onClickShowBanDialog(author.id, author.displayName, comment.id)}
|
||||
key={i}
|
||||
raised
|
||||
>
|
||||
<Icon name='not_interested' className={styles.banIcon} />
|
||||
{lang.t('comment.ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -36,13 +36,33 @@
|
||||
|
||||
.listItem {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
max-width: 660px;
|
||||
min-width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 14px;
|
||||
position: relative;
|
||||
transition: box-shadow 200ms;
|
||||
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.sideActions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
padding: 40px 18px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.itemHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -71,25 +91,20 @@
|
||||
|
||||
.created {
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
margin-left: 10px;
|
||||
font-size: 13px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.actionButton {
|
||||
transform: scale(.7);
|
||||
transform: scale(.8);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-top: 20px;
|
||||
flex: 1;
|
||||
font-size: 1em;
|
||||
color: rgba(0,0,0,.54);
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
font-size: 0.88em;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.flagged {
|
||||
@@ -143,3 +158,20 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.ban {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.banButton {
|
||||
width: 114px;
|
||||
letter-spacing: 1px;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,42 @@
|
||||
.header {
|
||||
background: #505050;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
min-height: 58px;
|
||||
}
|
||||
|
||||
.header > div {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #232323;
|
||||
background-color: #696969;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
min-width: 1280px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
height: 58px;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 170px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 170px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightPanel ul {
|
||||
list-style: none;
|
||||
line-height: 38px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.rightPanel li {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 33px;
|
||||
}
|
||||
|
||||
.rightPanel .settings {
|
||||
vertical-align: middle;
|
||||
border-radius: 3px;
|
||||
border: solid 1px #9e9e9e;
|
||||
line-height: 10px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.rightPanel .settings > div {
|
||||
@@ -45,3 +47,25 @@
|
||||
background: rgba(158, 158, 158, 0.69);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.navLink {
|
||||
padding: 0 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
background-color: transparent;
|
||||
transition: background-color 200ms;
|
||||
|
||||
&.active {
|
||||
background-color: #232323;
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
overflow: hidden;
|
||||
height: 58px !important;
|
||||
}
|
||||
|
||||
.nav .navLink {
|
||||
padding: 0 20px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {Logo} from './Logo';
|
||||
export default ({handleLogout}) => (
|
||||
<Header className={styles.header}>
|
||||
<Logo />
|
||||
<Navigation>
|
||||
<Navigation className={styles.nav}>
|
||||
<IndexLink className={styles.navLink} to="/admin"
|
||||
activeClassName={styles.active}>{lang.t('configure.moderate')}</IndexLink>
|
||||
<Link className={styles.navLink} to="/admin/community"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
.layout {
|
||||
max-width: 1170px;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
overflow: hidden;
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
.logo h1 {
|
||||
color: #272727;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
line-height: 60px;
|
||||
padding: 0 20px;
|
||||
color: #272727;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
line-height: 60px;
|
||||
padding-left: 13px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.logo span {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.logo {
|
||||
background: #E5E5E5;
|
||||
height: 100%;
|
||||
background: #E5E5E5;
|
||||
height: 100%;
|
||||
width: 128px;
|
||||
}
|
||||
|
||||
|
||||
.base {
|
||||
stroke: #E5E5E5;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {CoralLogo} from 'coral-ui';
|
||||
export const Logo = () => (
|
||||
<div className={styles.logo}>
|
||||
<h1>
|
||||
<CoralLogo stroke="#E5E5E5" />
|
||||
<CoralLogo className={styles.base} />
|
||||
<span>Talk</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,96 @@
|
||||
.roleButton {
|
||||
display: block;
|
||||
.container {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
padding-bottom: 200px;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
display: block;
|
||||
padding-left: 40px;
|
||||
width: auto;
|
||||
.leftColumn {
|
||||
padding: 42px 56px;
|
||||
width: 234px;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
width: calc(100% - 300px);
|
||||
padding: 34px 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.roleButton {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
background: white;
|
||||
width: 100%;
|
||||
padding: 9px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
background: white;
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
|
||||
i {
|
||||
color: #A1A1A1
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
padding: 0 2px 0 15px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.email {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dataTable {
|
||||
width: 100%;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
|
||||
th {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
th:nth-child(2), th:nth-child(3) {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.selectField {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 36px;
|
||||
background: #2c2c2c;
|
||||
padding: 10px 15px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
|
||||
> div {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.7px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import {Grid, Cell} from 'react-mdl';
|
||||
|
||||
import styles from './Community.css';
|
||||
import Table from './Table';
|
||||
import Loading from './Loading';
|
||||
import NoResults from './NoResults';
|
||||
import Pager from 'coral-ui/components/Pager';
|
||||
import {Pager} from 'coral-ui';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
@@ -33,17 +32,17 @@ const tableHeaders = [
|
||||
const Community = ({isFetching, commenters, ...props}) => {
|
||||
const hasResults = !isFetching && !!commenters.length;
|
||||
return (
|
||||
<Grid>
|
||||
<Cell col={2}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.leftColumn}>
|
||||
<form action="">
|
||||
<div className={`mdl-textfield ${styles.searchBox}`}>
|
||||
<label className="mdl-button mdl-js-button mdl-button--icon" htmlFor="commenters-search">
|
||||
<div className={`${styles.searchBox}`}>
|
||||
<label htmlFor="commenters-search">
|
||||
<i className="material-icons">search</i>
|
||||
</label>
|
||||
<div className="">
|
||||
<input
|
||||
id="commenters-search"
|
||||
className={`mdl-textfield__input ${styles.searchInput}`}
|
||||
className={`${styles.searchInput}`}
|
||||
type="text"
|
||||
value={props.searchValue}
|
||||
onKeyDown={props.onKeyDownHandler}
|
||||
@@ -52,8 +51,8 @@ const Community = ({isFetching, commenters, ...props}) => {
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Cell>
|
||||
<Cell col={6}>
|
||||
</div>
|
||||
<div className={styles.mainContent}>
|
||||
{ isFetching && <Loading /> }
|
||||
{ !hasResults && <NoResults /> }
|
||||
{ hasResults &&
|
||||
@@ -68,8 +67,8 @@ const Community = ({isFetching, commenters, ...props}) => {
|
||||
page={props.page}
|
||||
onNewPageHandler={props.onNewPageHandler}
|
||||
/>
|
||||
</Cell>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ class Table extends Component {
|
||||
</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>
|
||||
@@ -60,6 +61,7 @@ class Table extends Component {
|
||||
</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>
|
||||
|
||||
@@ -3,15 +3,8 @@ import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import styles from './Configure.css';
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemContent,
|
||||
ListItemAction,
|
||||
Textfield,
|
||||
Checkbox,
|
||||
Icon
|
||||
} from 'react-mdl';
|
||||
import {Textfield, Checkbox} from 'react-mdl';
|
||||
import {Card, Icon, Spinner} from 'coral-ui';
|
||||
|
||||
const TIMESTAMPS = {
|
||||
weeks: 60 * 60 * 24 * 7,
|
||||
@@ -71,35 +64,32 @@ const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => {
|
||||
|
||||
const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError, settings, errors}) => {
|
||||
if (fetchingSettings) {
|
||||
|
||||
/* maybe a spinner here at some point */
|
||||
return <p>Loading settings...</p>;
|
||||
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.commentSettingsSection}>
|
||||
<h3>{title}</h3>
|
||||
<List>
|
||||
<ListItem className={`${styles.configSetting} ${settings.moderation === 'pre' ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<ListItemAction>
|
||||
<Card className={`${styles.configSetting} ${settings.moderation === 'pre' ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
onChange={updateModeration(updateSettings, settings.moderation)}
|
||||
checked={settings.moderation === 'pre'} />
|
||||
</ListItemAction>
|
||||
<ListItemContent>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
|
||||
<p className={settings.moderation === 'pre' ? '' : styles.disabledSettingText}>
|
||||
{lang.t('configure.enable-pre-moderation-text')}
|
||||
</p>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={`${styles.configSetting} ${settings.charCountEnable ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<ListItemAction>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className={`${styles.configSetting} ${settings.charCountEnable ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
onChange={updateCharCountEnable(updateSettings, settings.charCountEnable)}
|
||||
checked={settings.charCountEnable} />
|
||||
</ListItemAction>
|
||||
<ListItemContent>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.settingsHeader}>{lang.t('configure.comment-count-header')}</div>
|
||||
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
|
||||
<span>{lang.t('configure.comment-count-text-pre')}</span>
|
||||
@@ -118,32 +108,44 @@ const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError
|
||||
</span>
|
||||
}
|
||||
</p>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem threeLine className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<ListItemAction>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? styles.enabledSetting : styles.disabledSetting}`}>
|
||||
<div className={styles.action}>
|
||||
<Checkbox
|
||||
onChange={updateInfoBoxEnable(updateSettings, settings.infoBoxEnable)}
|
||||
checked={settings.infoBoxEnable} />
|
||||
</ListItemAction>
|
||||
<ListItemContent>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{lang.t('configure.include-comment-stream')}
|
||||
<p>
|
||||
{lang.t('configure.include-comment-stream-desc')}
|
||||
</p>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
|
||||
<ListItemContent>
|
||||
<div className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
|
||||
<div className={styles.content}>
|
||||
<Textfield
|
||||
onChange={updateInfoBoxContent(updateSettings)}
|
||||
value={settings.infoBoxContent}
|
||||
label={lang.t('configure.include-text')}
|
||||
rows={3}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className={styles.configSettingInfoBox}>
|
||||
<div className={styles.content}>
|
||||
{lang.t('configure.closed-comments-desc')}
|
||||
<div>
|
||||
<Textfield
|
||||
onChange={updateInfoBoxContent(updateSettings)}
|
||||
value={settings.infoBoxContent}
|
||||
label={lang.t('configure.include-text')}
|
||||
onChange={updateClosedMessage(updateSettings)}
|
||||
value={settings.closedMessage}
|
||||
label={lang.t('configure.closed-comments-label')}
|
||||
rows={3}/>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={styles.configSettingInfoBox}>
|
||||
<ListItemContent>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className={`${styles.configSettingInfoBox}`}>
|
||||
<div className={styles.content}>
|
||||
{lang.t('configure.close-after')}
|
||||
<br />
|
||||
<Textfield
|
||||
@@ -163,19 +165,8 @@ const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError
|
||||
<Option value={'weeks'}>{lang.t('configure.weeks')}</Option>
|
||||
</SelectField>
|
||||
</div>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={styles.configSettingInfoBox}>
|
||||
<ListItemContent>
|
||||
{lang.t('configure.closed-comments-desc')}
|
||||
<Textfield
|
||||
onChange={updateClosedMessage(updateSettings)}
|
||||
value={settings.closedMessage}
|
||||
label={lang.t('configure.closed-comments-label')}
|
||||
rows={3}/>
|
||||
</ListItemContent>
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
.container {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
|
||||
h3 {
|
||||
color: black;
|
||||
font-size: 1.76em;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.leftColumn {
|
||||
width: 300px;
|
||||
padding: 42px 56px;
|
||||
width: 234px;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
width: calc(70% - 300px)
|
||||
}
|
||||
|
||||
.settingOption {
|
||||
cursor: pointer;
|
||||
width: calc(100% - 300px);
|
||||
padding: 10px 14px;
|
||||
box-sizing: border-box;
|
||||
max-width: 718px;
|
||||
}
|
||||
|
||||
.configSetting {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
height: 95px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
align-items: flex-start;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.settingsError {
|
||||
@@ -42,13 +45,13 @@
|
||||
}
|
||||
|
||||
.configSettingInfoBox {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
min-height: 100px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
height: auto;
|
||||
text-align: left;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.configSettingInfoBox p {
|
||||
@@ -77,7 +80,7 @@
|
||||
}
|
||||
|
||||
.charCountTexfieldEnabled {
|
||||
border-color: #4caf50;
|
||||
border-color: #00796b;
|
||||
}
|
||||
|
||||
.charCountTexfield:focus {
|
||||
@@ -85,11 +88,12 @@
|
||||
}
|
||||
|
||||
.changedSave {
|
||||
background-color:#4caf50;
|
||||
background-color: #00796B;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copiedText {
|
||||
color: #008000;
|
||||
color: #00796b;
|
||||
float: right;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
@@ -97,6 +101,7 @@
|
||||
|
||||
.copyButton {
|
||||
float: right;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.embedInput {
|
||||
@@ -113,8 +118,12 @@
|
||||
}
|
||||
|
||||
#bannedWordlist, #suspectWordlist {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.wordlistHeader {
|
||||
@@ -124,7 +133,7 @@
|
||||
}
|
||||
|
||||
.enabledSetting {
|
||||
border-left-color: #4caf50;
|
||||
border-left-color: #00796b;
|
||||
border-left-style: solid;
|
||||
border-left-width: 7px;
|
||||
}
|
||||
@@ -136,3 +145,23 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.saveBox {
|
||||
margin-top: 38px;
|
||||
}
|
||||
|
||||
.commentSettingsSection {
|
||||
padding-bottom: 200px;
|
||||
.action {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: inline-block;
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
fetchSettings,
|
||||
@@ -6,13 +6,8 @@ import {
|
||||
saveSettingsToServer,
|
||||
updateWordlist,
|
||||
} from '../../actions/settings';
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
ListItemContent,
|
||||
Button,
|
||||
Icon
|
||||
} from 'react-mdl';
|
||||
|
||||
import {Button, List, Item} from 'coral-ui';
|
||||
import styles from './Configure.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
@@ -21,7 +16,7 @@ import CommentSettings from './CommentSettings';
|
||||
import Wordlist from './Wordlist';
|
||||
import has from 'lodash/has';
|
||||
|
||||
class Configure extends React.Component {
|
||||
class Configure extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
@@ -30,6 +25,8 @@ class Configure extends React.Component {
|
||||
changed: false,
|
||||
errors: {}
|
||||
};
|
||||
|
||||
this.changeSection = this.changeSection.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount = () => {
|
||||
@@ -41,7 +38,7 @@ class Configure extends React.Component {
|
||||
this.setState({changed: false});
|
||||
}
|
||||
|
||||
changeSection = (activeSection) => () => {
|
||||
changeSection(activeSection) {
|
||||
this.setState({activeSection});
|
||||
}
|
||||
|
||||
@@ -99,7 +96,8 @@ class Configure extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const section = this.getSection(this.state.activeSection);
|
||||
const {activeSection} = this.state;
|
||||
const section = this.getSection(activeSection);
|
||||
|
||||
const showSave = Object.keys(this.state.errors).reduce(
|
||||
(bool, error) => this.state.errors[error] ? false : bool, this.state.changed);
|
||||
@@ -107,37 +105,40 @@ class Configure extends React.Component {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.leftColumn}>
|
||||
<List>
|
||||
<ListItem className={styles.settingOption}>
|
||||
<ListItemContent
|
||||
onClick={this.changeSection('comments')}
|
||||
icon='settings'>{lang.t('configure.comment-settings')}</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={styles.settingOption}>
|
||||
<ListItemContent
|
||||
onClick={this.changeSection('embed')}
|
||||
icon='code'>{lang.t('configure.embed-comment-stream')}</ListItemContent>
|
||||
</ListItem>
|
||||
<ListItem className={styles.settingOption}>
|
||||
<ListItemContent
|
||||
onClick={this.changeSection('wordlist')}
|
||||
icon='settings'>{lang.t('configure.wordlist')}</ListItemContent>
|
||||
</ListItem>
|
||||
<List onChange={this.changeSection} activeItem={activeSection}>
|
||||
<Item itemId='comments' icon="settings">
|
||||
{lang.t('configure.comment-settings')}
|
||||
</Item>
|
||||
<Item itemId='embed' icon='code'>
|
||||
{lang.t('configure.embed-comment-stream')}
|
||||
</Item>
|
||||
<Item itemId='wordlist' icon='settings'>
|
||||
{lang.t('configure.wordlist')}
|
||||
</Item>
|
||||
</List>
|
||||
<div className={styles.saveBox}>
|
||||
{
|
||||
showSave ?
|
||||
<Button
|
||||
raised
|
||||
onClick={this.saveSettings}
|
||||
className={styles.changedSave}>
|
||||
<Icon name='check' /> {lang.t('configure.save-changes')}
|
||||
</Button>
|
||||
: <Button
|
||||
raised
|
||||
disabled>
|
||||
{lang.t('configure.save-changes')}
|
||||
</Button>
|
||||
<Button
|
||||
raised
|
||||
onClick={this.saveSettings}
|
||||
className={styles.changedSave}
|
||||
icon='check'
|
||||
full
|
||||
>
|
||||
{lang.t('configure.save-changes')}
|
||||
</Button>
|
||||
:
|
||||
<Button
|
||||
raised
|
||||
disabled
|
||||
icon='check'
|
||||
full
|
||||
>
|
||||
{lang.t('configure.save-changes')}
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className={styles.mainContent}>
|
||||
|
||||
@@ -2,11 +2,7 @@ import React, {Component} from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import styles from './Configure.css';
|
||||
import {
|
||||
List,
|
||||
ListItem,
|
||||
Button
|
||||
} from 'react-mdl';
|
||||
import {Button, Card} from 'coral-ui';
|
||||
|
||||
class EmbedLink extends Component {
|
||||
|
||||
@@ -34,16 +30,16 @@ class EmbedLink extends Component {
|
||||
return (
|
||||
<div>
|
||||
<h3>{this.props.title}</h3>
|
||||
<List>
|
||||
<ListItem className={styles.configSettingEmbed}>
|
||||
<div>
|
||||
<Card shadow="2">
|
||||
<p>{lang.t('configure.copy-and-paste')}</p>
|
||||
<textarea rows={5} type='text' className={styles.embedInput} value={embedText} readOnly={true}/>
|
||||
<Button raised colored className={styles.copyButton} onClick={this.copyToClipBoard}>
|
||||
<Button raised className={styles.copyButton} onClick={this.copyToClipBoard} cStyle="black">
|
||||
{lang.t('embedlink.copy')}
|
||||
</Button>
|
||||
<div className={styles.copiedText}>{this.state.copied && 'Copied!'}</div>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@ import React from 'react';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import TagsInput from 'react-tagsinput';
|
||||
|
||||
import styles from './Configure.css';
|
||||
|
||||
import {Card} from 'react-mdl';
|
||||
import {Card} from 'coral-ui';
|
||||
|
||||
const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
|
||||
<div>
|
||||
<h3>{lang.t('configure.banned-words-title')}</h3>
|
||||
<Card id={styles.bannedWordlist} shadow={2}>
|
||||
<Card id={styles.bannedWordlist}>
|
||||
<p className={styles.wordlistHeader}>{lang.t('configure.banned-word-header')}</p>
|
||||
<p className={styles.wordlistDesc}>{lang.t('configure.banned-word-text')}</p>
|
||||
<TagsInput
|
||||
@@ -18,10 +16,11 @@ const Wordlist = ({suspectWords, bannedWords, onChangeWordlist}) => (
|
||||
inputProps={{placeholder: 'word or phrase'}}
|
||||
addOnPaste={true}
|
||||
pasteSplit={data => data.split(',').map(d => d.trim())}
|
||||
onChange={tags => onChangeWordlist('banned', tags)} />
|
||||
onChange={tags => onChangeWordlist('banned', tags)}
|
||||
/>
|
||||
</Card>
|
||||
<h3>{lang.t('configure.suspect-words-title')}</h3>
|
||||
<Card id={styles.suspectWordlist} shadow={2}>
|
||||
<Card id={styles.suspectWordlist}>
|
||||
<p className={styles.wordlistHeader}>{lang.t('configure.suspect-word-header')}</p>
|
||||
<p className={styles.wordlistDesc}>{lang.t('configure.suspect-word-text')}</p>
|
||||
<TagsInput
|
||||
|
||||
@@ -6,12 +6,32 @@
|
||||
}
|
||||
|
||||
.tabBar {
|
||||
background: #9E9E9E;
|
||||
background: #262626;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
color: white;
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
letter-spacing: 1px;
|
||||
transition: border-bottom 200ms;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
border-bottom: solid 5px #F36451;
|
||||
}
|
||||
|
||||
.active > span {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.active:after {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.showShortcuts {
|
||||
|
||||
@@ -12,14 +12,35 @@ const lang = new I18n(translations);
|
||||
|
||||
export default ({onTabClick, ...props}) => (
|
||||
<div>
|
||||
<div className='mdl-tabs mdl-js-tabs mdl-js-ripple-effect'>
|
||||
<div className='mdl-tabs'>
|
||||
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
|
||||
<a href='#pending' onClick={() => onTabClick('pending')}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.pending')}</a>
|
||||
<a href='#rejected' onClick={() => onTabClick('rejected')}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.rejected')}</a>
|
||||
<a href='#flagged' onClick={() => onTabClick('flagged')}
|
||||
className={`mdl-tabs__tab ${styles.tab}`}>{lang.t('modqueue.flagged')}</a>
|
||||
<a href='#pending'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onTabClick('pending');
|
||||
}}
|
||||
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'pending' ? styles.active : ''}`}
|
||||
>
|
||||
{lang.t('modqueue.pending')}
|
||||
</a>
|
||||
<a href='#rejected'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onTabClick('rejected');
|
||||
}}
|
||||
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'rejected' ? styles.active : ''}`}
|
||||
>
|
||||
{lang.t('modqueue.rejected')}
|
||||
</a>
|
||||
<a href='#flagged'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onTabClick('flagged');
|
||||
}}
|
||||
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'flagged' ? styles.active : ''}`}
|
||||
>
|
||||
{lang.t('modqueue.flagged')}
|
||||
</a>
|
||||
</div>
|
||||
<div className={`mdl-tabs__panel is-active ${styles.listContainer}`} id='pending'>
|
||||
{
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
}
|
||||
|
||||
.leftColumn {
|
||||
width: 200px;
|
||||
padding: 42px 56px;
|
||||
width: 234px;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
width: calc(90% - 200px);
|
||||
width: calc(100% - 300px);
|
||||
padding: 34px 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.searchIcon {
|
||||
@@ -18,11 +21,12 @@
|
||||
}
|
||||
|
||||
.searchBox {
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
padding: 9px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
width: 90%;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.searchBoxInput {
|
||||
@@ -48,6 +52,16 @@
|
||||
|
||||
.streamsTable {
|
||||
width: 100%;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
|
||||
th {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
th.status {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
@@ -55,18 +69,20 @@
|
||||
}
|
||||
|
||||
.statusMenu {
|
||||
border-radius: 3px;
|
||||
border-radius: 2px;
|
||||
width: 10em;
|
||||
text-align: center;
|
||||
float: right;
|
||||
border: 1px solid #ccc;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.7px;
|
||||
font-weight: 400;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.statusMenuOpen {
|
||||
padding: 10px;
|
||||
background-color: #4caf50;
|
||||
background-color: #268D81;
|
||||
}
|
||||
|
||||
.statusMenuIcon {
|
||||
@@ -75,9 +91,17 @@
|
||||
|
||||
.statusMenuClosed {
|
||||
padding: 10px;
|
||||
background-color: #000;
|
||||
background-color: #262626;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.radioGroup {
|
||||
margin-top: 5px;
|
||||
span {
|
||||
margin-bottom: 7px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,61 +103,63 @@ class Streams extends Component {
|
||||
render () {
|
||||
const {search, sort, filter} = this.state;
|
||||
const {assets} = this.props;
|
||||
|
||||
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>
|
||||
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')}>
|
||||
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')}>
|
||||
<Radio value='desc'>{lang.t('streams.newest')}</Radio>
|
||||
<Radio value='asc'>{lang.t('streams.oldest')}</Radio>
|
||||
</RadioGroup>
|
||||
<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>
|
||||
<div className={styles.mainContent}>
|
||||
<DataTable
|
||||
className={styles.streamsTable}
|
||||
rows={assets.ids.map((id) => assets.byId[id])}>
|
||||
<TableHeader name="title">{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>
|
||||
</div>
|
||||
|
||||
<div className={styles.mainContent}>
|
||||
<DataTable
|
||||
className={styles.streamsTable}
|
||||
rows={assets.ids.map((id) => assets.byId[id])}>
|
||||
<TableHeader name="title">{lang.t('streams.article')}</TableHeader>
|
||||
<TableHeader numeric name="publication_date" cellFormatter={this.renderDate}>
|
||||
{lang.t('streams.pubdate')}
|
||||
</TableHeader>
|
||||
<TableHeader numeric name="closedAt" cellFormatter={this.renderStatus}>
|
||||
{lang.t('streams.status')}
|
||||
</TableHeader>
|
||||
</DataTable>
|
||||
<Pager
|
||||
totalPages={Math.ceil((assets.count || 0) / limit)}
|
||||
page={this.state.page}
|
||||
onNewPageHandler={this.onPageClick}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,13 +46,13 @@
|
||||
"include-comment-stream": "Include Comment Stream Description for Readers.",
|
||||
"include-comment-stream-desc": "Write a message to be added to the top of your comment stream. Pose a topic, include community guidelines, etc.",
|
||||
"include-text": "Include your text here.",
|
||||
"comment-settings": "Comment Settings",
|
||||
"embed-comment-stream": "Embed Comment Stream",
|
||||
"comment-settings": "Settings",
|
||||
"embed-comment-stream": "Embed Stream",
|
||||
"banned-word-header": "Write the banned words list",
|
||||
"suspect-word-header": "Write the suspect words list",
|
||||
"banned-word-text": "Comments which contain these words or phrases (not case-sensitive) will be automatically removed from the comment stream. Type a word and press Enter or Tab to add. Optionally paste a comma-separated list.",
|
||||
"suspect-word-text": "Comments which contain these words or phrases (not case-sensitive) will be highlighted in the comment stream. Type a word and press Enter or Tab to add. Optionally paste a comma-separated list.",
|
||||
"wordlist": "Banned & Suspect Words",
|
||||
"wordlist": "Banned Words",
|
||||
"banned-words-title": "Banned words list",
|
||||
"suspect-words-title": "Suspect words list",
|
||||
"save-changes": "Save Changes",
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
display: inline-block;
|
||||
font-family: 'Roboto','Helvetica','Arial',sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
overflow: hidden;
|
||||
will-change: box-shadow,transform;
|
||||
-webkit-transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
|
||||
@@ -24,6 +22,14 @@
|
||||
line-height: 28px;
|
||||
vertical-align: middle;
|
||||
margin: 2px;
|
||||
letter-spacing: 0.7px;
|
||||
font-weight: 400;
|
||||
|
||||
i {
|
||||
margin-right: 13px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.type--black {
|
||||
@@ -69,7 +75,7 @@
|
||||
|
||||
.type--darkGrey {
|
||||
color: white;
|
||||
background: #696969;
|
||||
background: #616161;
|
||||
}
|
||||
|
||||
.type--darkGrey:hover {
|
||||
@@ -104,6 +110,5 @@
|
||||
}
|
||||
|
||||
.raised {
|
||||
background: rgba(158,158,158,.2);
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import React from 'react';
|
||||
import styles from './Button.css';
|
||||
import Icon from './Icon';
|
||||
|
||||
const Button = ({cStyle = 'local', children, className, raised, full, ...props}) => (
|
||||
const Button = ({cStyle = 'local', children, className, raised = false, full = false, icon = '', ...props}) => (
|
||||
<button
|
||||
className={`
|
||||
${styles.button}
|
||||
${styles[`type--${cStyle}`]}
|
||||
${className}
|
||||
${full && styles.full}
|
||||
${raised && styles.button}
|
||||
${full ? styles.full : ''}
|
||||
${raised ? styles.raised : ''}
|
||||
`}
|
||||
{...props}
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
.base {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
min-height: 200px;
|
||||
overflow: hidden;
|
||||
width: 330px;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.shadow--4 {
|
||||
box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.shadow--2{
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.shadow--3 {
|
||||
box-shadow: 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.2), 0 1px 8px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import styles from './Card.css';
|
||||
|
||||
export default ({children, className, shadow = 2, ...props}) => (
|
||||
<div className={`${styles.base} ${className} ${styles[`shadow--${shadow}`]}`} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
.base {
|
||||
height: 30px;
|
||||
width : 30px;
|
||||
}
|
||||
|
||||
.mark {
|
||||
stroke: #FFFFFF;
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import styles from './CoralLogo.css';
|
||||
|
||||
const CoralLogo = ({height = '30px', width = '30px', stroke = '#FFFFFF'}) => (
|
||||
<svg width={width} height={height} viewBox='0 0 381 391' version='1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink'>
|
||||
const CoralLogo = ({className = ''}) => (
|
||||
<svg className={`${styles.base} ${className}`} viewBox='0 0 381 391' version='1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink'>
|
||||
<g stroke='none' strokeWidth='1' fill='none' fillRule='evenodd'>
|
||||
<g id='Wordmark-Round' transform='translate(-1833.000000, -411.000000)' stroke={stroke} strokeWidth='22' strokeLinecap='round' strokeLinejoin='round'>
|
||||
<g id='Wordmark-Round' className={styles.mark} transform='translate(-1833.000000, -411.000000)' strokeWidth='22' strokeLinecap='round' strokeLinejoin='round'>
|
||||
<g id='coralProjectLogo-2-Copy-2' transform='translate(1842.000000, 421.000000)'>
|
||||
<g id='Layer_2' transform='translate(2.268750, 1.133903)'>
|
||||
<rect id='Rectangle-1' fill='#F47E6B' x='0' y='0' width='358.4625' height='368.518519' rx='40'>
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
.base {
|
||||
background: red;
|
||||
i {
|
||||
font-size: 30px;
|
||||
transform: translate(-14px,-12px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.type--approve {
|
||||
background: #00796b;
|
||||
color: rgba(255, 255, 255, 0.901961);
|
||||
background: #388E3C;
|
||||
color: rgba(255, 255, 255, 0.901961);
|
||||
}
|
||||
|
||||
.type--approve:hover {
|
||||
background: #40a244;
|
||||
}
|
||||
|
||||
.type--reject {
|
||||
background: #d32f2f ;
|
||||
color: rgba(255, 255, 255, 0.901961);
|
||||
background: #D32F2F ;
|
||||
color: rgba(255, 255, 255, 0.901961);
|
||||
}
|
||||
|
||||
.type--reject:hover {
|
||||
background: #e53333;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import styles from './FabButton.css';
|
||||
import {FABButton, Icon} from 'react-mdl';
|
||||
|
||||
const FabButton = ({cStyle = 'local', icon, className, ...props}) => (
|
||||
<FABButton className={`${styles[`type--${cStyle}`]} ${className ? className : ''}`} {...props}>
|
||||
<FABButton className={`${styles.base} ${styles[`type--${cStyle}`]} ${className ? className : ''}`} {...props}>
|
||||
<Icon name={icon} />
|
||||
</FABButton>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import {Icon as IconMDL} from 'react-mdl';
|
||||
|
||||
const Icon = ({className, name}) => (
|
||||
<IconMDL className={className} name={name} />
|
||||
);
|
||||
|
||||
export default Icon;
|
||||
@@ -0,0 +1,24 @@
|
||||
.base {
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
background-color: white;
|
||||
width: 208px;
|
||||
padding: 10px 14px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: white;
|
||||
background-color: #262626;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 13px;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './Item.css';
|
||||
import Icon from './Icon';
|
||||
|
||||
export default ({children, itemId, active, onItemClick, className = '', icon}) => (
|
||||
<li
|
||||
className={`${styles.base} ${className} ${active ? styles.active : ''}`}
|
||||
onClick={() => onItemClick(itemId)}
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
.base {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import React, {Component} from 'react';
|
||||
import styles from './List.css';
|
||||
|
||||
export default class List extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClickItem = this.handleClickItem.bind(this);
|
||||
}
|
||||
|
||||
handleClickItem(itemId) {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children, activeItem, className = ''} = this.props;
|
||||
return (
|
||||
<ul className={`${styles.base} ${className}`}>
|
||||
{React.Children.toArray(children)
|
||||
.filter(child => !child.props.restricted)
|
||||
.map((child, i) =>
|
||||
React.cloneElement(child, {
|
||||
i,
|
||||
active: child.props.itemId === activeItem,
|
||||
onItemClick: this.handleClickItem,
|
||||
})
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,19 @@
|
||||
.li {
|
||||
.pager {
|
||||
text-align: center;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
padding: 0;
|
||||
min-width: 30px;
|
||||
color: white;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.current {
|
||||
background: #e3edf3;
|
||||
}
|
||||
background: #696969;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
@@ -2,19 +2,18 @@ import React, {PropTypes} from 'react';
|
||||
import styles from './Pager.css';
|
||||
|
||||
const Rows = (curr, total, onClickHandler) => Array.from(Array(total)).map((e, i) =>
|
||||
<li className={`mdl-button mdl-js-button ${styles.li} ${curr === i ? styles.current : ''}`}
|
||||
<li className={curr === i ? styles.current : ''}
|
||||
key={i} onClick={() => onClickHandler(i + 1)}>
|
||||
{i + 1}
|
||||
</li>
|
||||
);
|
||||
|
||||
const Pager = ({totalPages, page, onNewPageHandler}) => (
|
||||
<div className="pager">
|
||||
<div className={styles.pager}>
|
||||
<ul>
|
||||
{
|
||||
(totalPages > page && totalPages > 1) ?
|
||||
<li
|
||||
className={`mdl-button mdl-js-button ${styles.li}`}
|
||||
onClick={() => onNewPageHandler(page - 1)}>
|
||||
Prev
|
||||
</li>
|
||||
@@ -25,7 +24,6 @@ const Pager = ({totalPages, page, onNewPageHandler}) => (
|
||||
{
|
||||
(page < totalPages && totalPages > 1) ?
|
||||
<li
|
||||
className={`mdl-button mdl-js-button ${styles.li}`}
|
||||
onClick={() => onNewPageHandler(page + 1)}>
|
||||
Next
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import styles from './TabBar.css';
|
||||
|
||||
export class TabBar extends React.Component {
|
||||
class TabBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClickTab = this.handleClickTab.bind(this);
|
||||
|
||||
@@ -9,3 +9,8 @@ export {default as Spinner} from './components/Spinner';
|
||||
export {default as Tooltip} from './components/Tooltip';
|
||||
export {default as PopupMenu} from './components/PopupMenu';
|
||||
export {default as Checkbox} from './components/Checkbox';
|
||||
export {default as Icon} from './components/Icon';
|
||||
export {default as List} from './components/List';
|
||||
export {default as Item} from './components/Item';
|
||||
export {default as Card} from './components/Card';
|
||||
export {default as Pager} from './components/Pager';
|
||||
|
||||
+3
-1
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
|
||||
<meta property="csrf" content="<%= csrfToken %>">
|
||||
<title>Talk - Coral Admin</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="https://code.getmdl.io/1.2.1/material.indigo-pink.min.css">
|
||||
<style media="screen">
|
||||
@@ -12,7 +13,8 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
background-color: #FAFAFA;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
/* putting this here until I can get webpack to behave */
|
||||
.react-tagsinput {
|
||||
|
||||
Reference in New Issue
Block a user