Merge branch 'master' into registration-fix

This commit is contained in:
David Erwin
2017-01-19 15:20:52 -05:00
committed by GitHub
40 changed files with 730 additions and 315 deletions
@@ -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>
+16 -16
View File
@@ -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;
}
}
+41 -17
View File
@@ -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;
}
+19 -12
View File
@@ -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;
}
+1 -1
View File
@@ -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>;
);
}
}
+3 -3
View File
@@ -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",
+9 -4
View File
@@ -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);
}
+5 -3
View File
@@ -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>
);
+35
View File
@@ -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);
}
+8
View File
@@ -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>
);
+8
View File
@@ -0,0 +1,8 @@
.base {
height: 30px;
width : 30px;
}
.mark {
stroke: #FFFFFF;
}
+4 -3
View File
@@ -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'>
+20 -4
View File
@@ -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;
}
+1 -1
View File
@@ -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>
);
+8
View File
@@ -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;
+24
View File
@@ -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;
}
}
+13
View File
@@ -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>
);
+4
View File
@@ -0,0 +1,4 @@
.base {
padding: 0;
margin: 0;
}
+32
View File
@@ -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>
);
}
}
+14 -5
View File
@@ -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 -4
View File
@@ -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 -1
View File
@@ -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);
+5
View File
@@ -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
View File
@@ -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 {