Merged in frontend-table-component (pull request #37)
Frontend table component + Implementation
This commit is contained in:
commit
cd49405f61
|
@ -8,7 +8,7 @@ gulp.task('browserSync', function() {
|
|||
|
||||
browserSync({
|
||||
proxy: 'localhost:' + config.serverport,
|
||||
startPath: 'app'
|
||||
startPath: '/'
|
||||
});
|
||||
|
||||
});
|
|
@ -21,7 +21,7 @@ var util = require('gulp-util');
|
|||
function buildScript(file, watch) {
|
||||
|
||||
var bundler = browserify({
|
||||
entries: [config.sourceDir + 'app/' + file],
|
||||
entries: [config.sourceDir + file],
|
||||
debug: !global.isProd,
|
||||
insertGlobalVars: {
|
||||
noFixtures: function() {
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"app-module-path": "^1.0.3",
|
||||
"classnames": "^2.1.3",
|
||||
"jquery": "^2.1.4",
|
||||
"keycode": "^2.1.4",
|
||||
"localStorage": "^1.0.3",
|
||||
"lodash": "^3.10.0",
|
||||
"messageformat": "^0.2.2",
|
||||
|
|
|
@ -11,42 +11,55 @@ const SessionActions = requireUnit('actions/session-actions', {
|
|||
});
|
||||
|
||||
describe('Session Actions,', function () {
|
||||
APICallMock.call.returns('API_RESULT');
|
||||
|
||||
describe('login action', function () {
|
||||
it('should return LOGIN with with API_RESULT promise', function () {
|
||||
APICallMock.call.reset();
|
||||
it('should return LOGIN with with a result promise', function () {
|
||||
APICallMock.call.returns({
|
||||
then: function (resolve) {
|
||||
resolve({
|
||||
data: {
|
||||
userId: 14
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let loginData = {
|
||||
email: 'SOME_EMAIL',
|
||||
password: 'SOME_PASSWORD',
|
||||
remember: false
|
||||
};
|
||||
|
||||
expect(SessionActions.login(loginData)).to.deep.equal({
|
||||
type: 'LOGIN',
|
||||
payload: 'API_RESULT'
|
||||
});
|
||||
|
||||
expect(SessionActions.login(loginData).type).to.equal('LOGIN');
|
||||
expect(storeMock.dispatch).to.have.been.calledWithMatch({type: 'USER_DATA'});
|
||||
expect(APICallMock.call).to.have.been.calledWith({
|
||||
path: '/user/login',
|
||||
data: loginData
|
||||
path: '/user/get',
|
||||
data: {
|
||||
userId: 14
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('autoLogin action', function () {
|
||||
it('should return LOGIN_AUTO with remember data from sessionStore', function () {
|
||||
APICallMock.call.reset();
|
||||
APICallMock.call.returns({
|
||||
then: function (resolve) {
|
||||
resolve({
|
||||
data: {
|
||||
userId: 14
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
sessionStoreMock.getRememberData.returns({
|
||||
token: 'SOME_TOKEN',
|
||||
userId: 'SOME_ID',
|
||||
expiration: 'SOME_EXPIRATION'
|
||||
});
|
||||
|
||||
expect(SessionActions.autoLogin()).to.deep.equal({
|
||||
type: 'LOGIN_AUTO',
|
||||
payload: 'API_RESULT'
|
||||
});
|
||||
expect(SessionActions.autoLogin().type).to.equal('LOGIN_AUTO');
|
||||
expect(storeMock.dispatch).to.have.been.calledWithMatch({type: 'USER_DATA'});
|
||||
expect(APICallMock.call).to.have.been.calledWith({
|
||||
path: '/user/login',
|
||||
data: {
|
||||
|
@ -60,6 +73,7 @@ describe('Session Actions,', function () {
|
|||
|
||||
describe('logout action', function () {
|
||||
it('should return LOGOUT and call /user/logout', function () {
|
||||
APICallMock.call.returns('API_RESULT');
|
||||
APICallMock.call.reset();
|
||||
|
||||
expect(SessionActions.logout()).to.deep.equal({
|
||||
|
|
|
@ -9,6 +9,10 @@ export default {
|
|||
payload: API.call({
|
||||
path: '/user/login',
|
||||
data: loginData
|
||||
}).then((result) => {
|
||||
store.dispatch(this.getUserData(result.data.userId));
|
||||
|
||||
return result;
|
||||
})
|
||||
};
|
||||
},
|
||||
|
@ -25,6 +29,10 @@ export default {
|
|||
rememberToken: rememberData.token,
|
||||
isAutomatic: true
|
||||
}
|
||||
}).then((result) => {
|
||||
store.dispatch(this.getUserData(result.data.userId));
|
||||
|
||||
return result;
|
||||
})
|
||||
};
|
||||
},
|
||||
|
@ -39,6 +47,18 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
getUserData(userId) {
|
||||
return {
|
||||
type: 'USER_DATA',
|
||||
payload: API.call({
|
||||
path: '/user/get',
|
||||
data: {
|
||||
userId: userId
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
initSession() {
|
||||
return {
|
||||
type: 'CHECK_SESSION',
|
||||
|
|
|
@ -28,8 +28,8 @@ class App extends React.Component {
|
|||
redirectIfPathIsNotValid(props) {
|
||||
const validations = {
|
||||
languageChanged: props.config.language !== this.props.config.language,
|
||||
loggedIn: !_.includes(props.location.pathname, '/app/dashboard') && props.session.logged,
|
||||
loggedOut: _.includes(props.location.pathname, '/app/dashboard') && !props.session.logged
|
||||
loggedIn: !_.includes(props.location.pathname, '/dashboard') && props.session.logged,
|
||||
loggedOut: _.includes(props.location.pathname, '/dashboard') && !props.session.logged
|
||||
};
|
||||
|
||||
if (validations.languageChanged) {
|
||||
|
@ -37,11 +37,11 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
if (validations.loggedOut) {
|
||||
browserHistory.push('/app');
|
||||
browserHistory.push('/');
|
||||
}
|
||||
|
||||
if (validations.loggedIn) {
|
||||
browserHistory.push('/app/dashboard');
|
||||
browserHistory.push('/dashboard');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ const history = syncHistoryWithStore(browserHistory, store);
|
|||
|
||||
export default (
|
||||
<Router history={history}>
|
||||
<Route component={App} path='/'>
|
||||
<Route path='/app' component={MainLayout}>
|
||||
<Route component={App}>
|
||||
<Route path='/' component={MainLayout}>
|
||||
<IndexRoute component={MainHomePage} />
|
||||
<Route path='signup' component={MainSignUpPage}/>
|
||||
<Route path='recover-password' component={MainRecoverPasswordPage}/>
|
||||
|
@ -40,7 +40,7 @@ export default (
|
|||
<Route path='edit-profile' component={DashboardEditProfilePage}/>
|
||||
|
||||
<Route path='article' component={DashboardArticlePage}/>
|
||||
<Route path='ticket' component={DashboardTicketPage}/>
|
||||
<Route path='ticket/:ticketNumber' component={DashboardTicketPage}/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ import React from 'react';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import DashboardMenu from 'app/main/dashboard/dashboard-menu';
|
||||
import Widget from 'core-components/widget';
|
||||
|
||||
class DashboardLayout extends React.Component {
|
||||
|
||||
render() {
|
||||
return (this.props.session.logged) ? (
|
||||
<div>
|
||||
<div><DashboardMenu location={this.props.location} /></div>
|
||||
<div>{this.props.children}</div>
|
||||
<div className="dashboard">
|
||||
<div className="dashboard__menu col-md-3"><DashboardMenu location={this.props.location} /></div>
|
||||
<Widget className="dashboard__content col-md-9">
|
||||
{this.props.children}
|
||||
</Widget>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
.dashboard {
|
||||
&__menu {
|
||||
|
||||
}
|
||||
|
||||
&__content {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,14 +1,76 @@
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import Table from 'core-components/table';
|
||||
import Button from 'core-components/button';
|
||||
|
||||
class DashboardListTicketsPage extends React.Component {
|
||||
static propTypes = {
|
||||
tickets: React.PropTypes.arrayOf(React.PropTypes.object)
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
tickets: []
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
DASHBOARD TICKET LIST
|
||||
<div className="dashboard-ticket-list">
|
||||
<div className="dashboard-ticket-list__header">Tickets</div>
|
||||
<Table headers={this.getTableHeaders()} rows={this.getTableRows()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getTableHeaders() {
|
||||
return [
|
||||
{
|
||||
key: 'number',
|
||||
value: 'Number',
|
||||
className: 'dashboard-ticket-list__number col-md-1'
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
value: 'Title',
|
||||
className: 'dashboard-ticket-list__title col-md-6'
|
||||
},
|
||||
{
|
||||
key: 'department',
|
||||
value: 'Department',
|
||||
className: 'dashboard-ticket-list__department col-md-3'
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
value: 'Date',
|
||||
className: 'dashboard-ticket-list__date col-md-2'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getTableRows() {
|
||||
return this.props.tickets.map(this.gerTicketTableObject.bind(this));
|
||||
}
|
||||
|
||||
gerTicketTableObject(ticket) {
|
||||
let titleText = (ticket.unread) ? ticket.title + ' (1)' : ticket.title;
|
||||
|
||||
return {
|
||||
number: '#' + ticket.ticketNumber,
|
||||
title: (
|
||||
<Button className="dashboard-ticket-list__title-link" type="clean" route={{to: '/dashboard/ticket/' + ticket.ticketNumber}}>
|
||||
{titleText}
|
||||
</Button>
|
||||
),
|
||||
department: ticket.department.name,
|
||||
date: ticket.date,
|
||||
highlighted: ticket.unread
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default DashboardListTicketsPage;
|
||||
|
||||
export default connect((store) => {
|
||||
return {
|
||||
tickets: store.session.userTickets
|
||||
};
|
||||
})(DashboardListTicketsPage);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
@import "../../../../scss/vars";
|
||||
|
||||
.dashboard-ticket-list {
|
||||
|
||||
&__header {
|
||||
text-align: left;
|
||||
font-variant: small-caps;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&__number {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__title {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__department {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__date {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__title-link:hover,
|
||||
&__title-link:focus {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Menu from 'core-components/menu';
|
||||
import {dispatch} from 'app/store';
|
||||
import SessionActions from 'actions/session-actions';
|
||||
import i18n from 'lib-app/i18n';
|
||||
|
||||
let dashboardRoutes = [
|
||||
{ path: '/app/dashboard', text: 'Ticket List' },
|
||||
{ path: '/app/dashboard/create-ticket', text: 'Create Ticket' },
|
||||
{ path: '/app/dashboard/articles', text: 'View Articles' },
|
||||
{ path: '/app/dashboard/edit-profile', text: 'Edit Profile' }
|
||||
];
|
||||
import Menu from 'core-components/menu';
|
||||
|
||||
class DashboardMenu extends React.Component {
|
||||
static contextTypes = {
|
||||
|
@ -27,30 +24,62 @@ class DashboardMenu extends React.Component {
|
|||
|
||||
getProps() {
|
||||
return {
|
||||
header: 'Dashboard',
|
||||
items: this.getMenuItems(),
|
||||
selectedIndex: this.getSelectedIndex(),
|
||||
onItemClick: this.goToPathByIndex.bind(this)
|
||||
onItemClick: this.onItemClick.bind(this),
|
||||
tabbable: true,
|
||||
type: 'secondary'
|
||||
};
|
||||
}
|
||||
|
||||
getMenuItems() {
|
||||
return dashboardRoutes.map(this.getMenuItem.bind(this));
|
||||
let items = this.getDashboardRoutes().map(this.getMenuItem.bind(this));
|
||||
|
||||
items.push(this.getCloseSessionItem());
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
getMenuItem(item) {
|
||||
return {
|
||||
content: item.text
|
||||
content: item.text,
|
||||
icon: item.icon
|
||||
};
|
||||
}
|
||||
|
||||
getCloseSessionItem() {
|
||||
return {
|
||||
content: i18n('CLOSE_SESSION'),
|
||||
icon: 'lock'
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedIndex() {
|
||||
let pathname = this.props.location.pathname;
|
||||
|
||||
return _.findIndex(dashboardRoutes, {path: pathname});
|
||||
return _.findIndex(this.getDashboardRoutes(), {path: pathname});
|
||||
}
|
||||
|
||||
onItemClick(itemIndex) {
|
||||
if (itemIndex < this.getDashboardRoutes().length) {
|
||||
this.goToPathByIndex(itemIndex)
|
||||
} else {
|
||||
dispatch(SessionActions.logout());
|
||||
}
|
||||
}
|
||||
|
||||
goToPathByIndex(itemIndex) {
|
||||
this.context.router.push(dashboardRoutes[itemIndex].path);
|
||||
this.context.router.push(this.getDashboardRoutes()[itemIndex].path);
|
||||
}
|
||||
|
||||
getDashboardRoutes() {
|
||||
return [
|
||||
{ path: '/dashboard', text: i18n('TICKET_LIST'), icon: 'file-text-o' },
|
||||
{ path: '/dashboard/create-ticket', text: i18n('CREATE_TICKET'), icon: 'plus' },
|
||||
{ path: '/dashboard/articles', text: i18n('VIEW_ARTICLES'), icon: 'book' },
|
||||
{ path: '/dashboard/edit-profile', text: i18n('EDIT_PROFILE'), icon: 'pencil' }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,16 +34,16 @@ class MainLayoutHeader extends React.Component {
|
|||
|
||||
if (this.props.session.logged) {
|
||||
result = (
|
||||
<div className="main-layout-header--login-links">
|
||||
Welcome, John
|
||||
<Button type="clean" onClick={this.logout.bind(this)}>(Close Session)</Button>
|
||||
<div className="main-layout-header__login-links">
|
||||
{i18n('WELCOME')},
|
||||
<span className="main-layout-header__user-name"> {this.props.session.userName}</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
result = (
|
||||
<div className="main-layout-header--login-links">
|
||||
<Button type="clean" route={{to:'/app'}}>{i18n('LOG_IN')}</Button>
|
||||
<Button type="clean" route={{to:'/app/signup'}}>Sign up</Button>
|
||||
<div className="main-layout-header__login-links">
|
||||
<Button type="clean" route={{to:'/'}}>{i18n('LOG_IN')}</Button>
|
||||
<Button type="clean" route={{to:'/signup'}}>Sign up</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class MainLayoutHeader extends React.Component {
|
|||
|
||||
getLanguageSelectorProps() {
|
||||
return {
|
||||
className: 'main-layout-header--languages',
|
||||
className: 'main-layout-header__languages',
|
||||
items: this.getLanguageList(),
|
||||
selectedIndex: Object.values(codeLanguages).indexOf(this.props.config.language),
|
||||
onChange: this.changeLanguage.bind(this)
|
||||
|
@ -74,10 +74,6 @@ class MainLayoutHeader extends React.Component {
|
|||
|
||||
this.props.dispatch(ConfigActions.changeLanguage(codeLanguages[language]));
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.props.dispatch(SessionActions.logout());
|
||||
}
|
||||
}
|
||||
|
||||
export default connect((store) => {
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
height: 32px;
|
||||
width: 100%;
|
||||
|
||||
&--login-links {
|
||||
&__user-name {
|
||||
color: $primary-red;
|
||||
}
|
||||
|
||||
&__login-links {
|
||||
border-top-left-radius: 4px;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
|
@ -14,7 +18,7 @@
|
|||
padding: 5px 20px 0 10px;
|
||||
}
|
||||
|
||||
&--languages {
|
||||
&__languages {
|
||||
float: right;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
|
|
|
@ -74,7 +74,7 @@ class MainRecoverPasswordPage extends React.Component {
|
|||
}
|
||||
|
||||
onPasswordRecovered() {
|
||||
setTimeout(() => {this.props.history.push('/app')}, 2000);
|
||||
setTimeout(() => {this.props.history.push('/')}, 2000);
|
||||
this.setState({
|
||||
recoverStatus: 'valid',
|
||||
loading: false
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import keyCode from 'keycode';
|
||||
|
||||
import callback from 'lib-core/callback';
|
||||
import getIcon from 'lib-core/get-icon';
|
||||
|
@ -87,7 +88,7 @@ class CheckBox extends React.Component {
|
|||
}
|
||||
|
||||
handleIconKeyDown(event) {
|
||||
if (event.keyCode == 32) {
|
||||
if (event.keyCode === keyCode('SPACE')) {
|
||||
event.preventDefault();
|
||||
|
||||
callback(this.handleChange.bind(this), this.props.onChange)({
|
||||
|
|
|
@ -1,33 +1,50 @@
|
|||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import keyCode from 'keycode';
|
||||
|
||||
import Icon from 'core-components/icon';
|
||||
|
||||
class Menu extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
header: React.PropTypes.string,
|
||||
type: React.PropTypes.oneOf(['primary', 'secondary']),
|
||||
items: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
content: React.PropTypes.string.isRequired,
|
||||
icon: React.PropTypes.string
|
||||
})).isRequired,
|
||||
selectedIndex: React.PropTypes.number
|
||||
selectedIndex: React.PropTypes.number,
|
||||
tabbable: React.PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
type: 'primary',
|
||||
selectedIndex: 0
|
||||
selectedIndex: 0,
|
||||
tabbable: false
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ul {...this.getProps()}>
|
||||
{this.props.items.map(this.renderListItem.bind(this))}
|
||||
</ul>
|
||||
<div className={this.getClass()}>
|
||||
{this.renderHeader()}
|
||||
<ul {...this.getProps()}>
|
||||
{this.props.items.map(this.renderListItem.bind(this))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
let header = null;
|
||||
|
||||
if (this.props.header) {
|
||||
header = <div className="menu__header">{this.props.header}</div>;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
renderListItem(item, index) {
|
||||
let iconNode = null;
|
||||
|
||||
|
@ -45,11 +62,13 @@ class Menu extends React.Component {
|
|||
getProps() {
|
||||
var props = _.clone(this.props);
|
||||
|
||||
props.className = this.getClass();
|
||||
props.className = 'menu__list';
|
||||
|
||||
delete props.header;
|
||||
delete props.items;
|
||||
delete props.onItemClick;
|
||||
delete props.selectedIndex;
|
||||
delete props.tabbable;
|
||||
delete props.type;
|
||||
|
||||
return props;
|
||||
|
@ -69,7 +88,9 @@ class Menu extends React.Component {
|
|||
getItemProps(index) {
|
||||
return {
|
||||
className: this.getItemClass(index),
|
||||
onClick: this.handleItemClick.bind(this, index),
|
||||
onClick: this.onItemClick.bind(this, index),
|
||||
tabIndex: (this.props.tabbable) ? '0' : null,
|
||||
onKeyDown: this.onKeyDown.bind(this, index),
|
||||
key: index
|
||||
};
|
||||
}
|
||||
|
@ -83,7 +104,16 @@ class Menu extends React.Component {
|
|||
return classNames(classes);
|
||||
}
|
||||
|
||||
handleItemClick(index) {
|
||||
onKeyDown(index, event) {
|
||||
let enterKey = keyCode('ENTER');
|
||||
let spaceKey = keyCode('SPACE');
|
||||
|
||||
if(event.keyCode === enterKey || event.keyCode === spaceKey) {
|
||||
this.onItemClick(index);
|
||||
}
|
||||
}
|
||||
|
||||
onItemClick(index) {
|
||||
if (this.props.onItemClick) {
|
||||
this.props.onItemClick(index);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.menu {
|
||||
background-color: white;
|
||||
color: $dark-grey;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
cursor: pointer;
|
||||
|
||||
&__list {
|
||||
background-color: white;
|
||||
color: $dark-grey;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__list-item {
|
||||
padding: 8px;
|
||||
|
@ -28,5 +31,14 @@
|
|||
.menu__list-item:hover {
|
||||
background-color: $secondary-blue;
|
||||
}
|
||||
|
||||
.menu__header {
|
||||
padding: 8px;
|
||||
background-color: $primary-blue;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
class Table extends React.Component {
|
||||
static propTypes = {
|
||||
headers: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||
key: React.PropTypes.string,
|
||||
value: React.PropTypes.string,
|
||||
className: React.PropTypes.string
|
||||
})),
|
||||
rows: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
type: React.PropTypes.oneOf(['default'])
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
type: 'default'
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<table className="table table-responsive">
|
||||
<thead>
|
||||
<tr className="table__header">
|
||||
{this.props.headers.map(this.renderHeaderColumn.bind(this))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.props.rows.map(this.renderRow.bind(this))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeaderColumn(header) {
|
||||
let classes = {
|
||||
'table__header-column': true,
|
||||
[header.className]: (header.className)
|
||||
};
|
||||
|
||||
return (
|
||||
<th className={classNames(classes)} key={header.key}>{header.value}</th>
|
||||
);
|
||||
}
|
||||
|
||||
renderRow(row, index) {
|
||||
let headersKeys = this.props.headers.map(header => header.key);
|
||||
|
||||
return (
|
||||
<tr className={this.getRowClass(row)} key={index}>
|
||||
{headersKeys.map(this.renderCell.bind(this, row))}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderCell(row, key, index) {
|
||||
let classes = {
|
||||
'table__cell': true,
|
||||
[this.props.headers[index].className]: (this.props.headers[index].className)
|
||||
};
|
||||
|
||||
return (
|
||||
<td className={classNames(classes)} key={key}>{row[key]}</td>
|
||||
);
|
||||
}
|
||||
|
||||
getRowClass(row) {
|
||||
let classes = {
|
||||
'table__row': true,
|
||||
'table__row-highlighted': row.highlighted
|
||||
};
|
||||
|
||||
return classNames(classes);
|
||||
}
|
||||
}
|
||||
|
||||
export default Table;
|
|
@ -0,0 +1,46 @@
|
|||
@import "../scss/vars";
|
||||
|
||||
.table {
|
||||
|
||||
&__header {
|
||||
background-color: $primary-blue;
|
||||
color: white;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__header-column {
|
||||
font-weight: normal;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-right-radius: 4px
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
border: 0;
|
||||
color: #B8B8B8;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: #F1F1F1;
|
||||
}
|
||||
|
||||
&-highlighted {
|
||||
color: $secondary-blue;
|
||||
font-weight: bold;
|
||||
background-color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__cell00 {
|
||||
border: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
|
@ -103,5 +103,107 @@ module.exports = [
|
|||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/user/get',
|
||||
time: 100,
|
||||
response: function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: {
|
||||
name: 'Haskell Curry',
|
||||
email: 'haskell@lambda.com',
|
||||
tickets: [
|
||||
{
|
||||
ticketNumber: '445441',
|
||||
title: 'Problem with installation',
|
||||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 2,
|
||||
name: 'Environment Setup'
|
||||
},
|
||||
date: '15 Apr 2016',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
language: 'en',
|
||||
unread: true,
|
||||
closed: false,
|
||||
author: {
|
||||
id: 12,
|
||||
name: 'Haskell Curry',
|
||||
email: 'haskell@lambda.com'
|
||||
},
|
||||
owner: {
|
||||
id: 15,
|
||||
name: 'Steve Jobs',
|
||||
email: 'steve@jobs.com'
|
||||
},
|
||||
comments: [
|
||||
{
|
||||
content: 'Do you have apache installed? It generally happens if you dont have apache.',
|
||||
author: {
|
||||
id: 15,
|
||||
name: 'Steve Jobs',
|
||||
email: 'jobs@steve.com',
|
||||
staff: true
|
||||
},
|
||||
date: '12 Dec 2016',
|
||||
file: ''
|
||||
},
|
||||
{
|
||||
content: 'I have already installed apache, but the problem persists',
|
||||
author: {
|
||||
id: 12,
|
||||
name: 'Haskell Curry',
|
||||
steve: 'haskell@lambda.com',
|
||||
staff: false
|
||||
},
|
||||
date: '12 Dec 2016',
|
||||
file: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
ticketNumber: '878552',
|
||||
title: 'Lorem ipsum door',
|
||||
content: 'I had a problem with the installation of the php server',
|
||||
department: {
|
||||
id: 2,
|
||||
name: 'Environment Setup'
|
||||
},
|
||||
date: '15 Apr 2016',
|
||||
file: 'http://www.opensupports.com/some_file.zip',
|
||||
language: 'en',
|
||||
unread: false,
|
||||
closed: false,
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
email: 'haskell@lambda.com'
|
||||
},
|
||||
owner: {
|
||||
name: 'Steve Jobs'
|
||||
},
|
||||
comments: [
|
||||
{
|
||||
content: 'Do you have apache installed? It generally happens if you dont have apache.',
|
||||
author: {
|
||||
name: 'Steve Jobs',
|
||||
email: 'jobs@steve.com',
|
||||
staff: true
|
||||
}
|
||||
},
|
||||
{
|
||||
content: 'I have already installed apache, but the problem persists',
|
||||
author: {
|
||||
name: 'Haskell Curry',
|
||||
steve: 'haskell@lambda.com',
|
||||
staff: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
||||
];
|
|
@ -1,4 +1,5 @@
|
|||
export default {
|
||||
'WELCOME': 'Welcome',
|
||||
'SUBMIT': 'Submit',
|
||||
'LOG_IN': 'Log in',
|
||||
'SIGN_UP': 'Sign up',
|
||||
|
@ -8,6 +9,11 @@ export default {
|
|||
'NEW_PASSWORD': 'New password',
|
||||
'REPEAT_NEW_PASSWORD': 'Repeat new password',
|
||||
'BACK_LOGIN_FORM': 'Back to login form',
|
||||
'TICKET_LIST': 'Ticket List',
|
||||
'CREATE_TICKET': 'Create Ticket',
|
||||
'VIEW_ARTICLES': 'View Articles',
|
||||
'EDIT_PROFILE': 'Edit Profile',
|
||||
'CLOSE_SESSION': 'Close session',
|
||||
|
||||
//ERRORS
|
||||
'EMAIL_NOT_EXIST': 'Email does not exist',
|
||||
|
|
|
@ -4,8 +4,8 @@ import { Provider } from 'react-redux';
|
|||
|
||||
import SessionActions from 'actions/session-actions';
|
||||
import ConfigActions from 'actions/config-actions';
|
||||
import routes from './Routes';
|
||||
import store from './store';
|
||||
import routes from 'app/Routes';
|
||||
import store from 'app/store';
|
||||
|
||||
if ( process.env.NODE_ENV !== 'production' ) {
|
||||
// Enable React devtools
|
|
@ -1,5 +1,8 @@
|
|||
export default {
|
||||
call: stub().returns(new Promise(function (resolve) {
|
||||
resolve();
|
||||
resolve({
|
||||
status: 'success',
|
||||
data: {}
|
||||
});
|
||||
}))
|
||||
};
|
|
@ -29,6 +29,17 @@ class SessionStore {
|
|||
closeSession() {
|
||||
this.removeItem('userId');
|
||||
this.removeItem('token');
|
||||
|
||||
this.clearRememberData();
|
||||
this.clearUserData();
|
||||
}
|
||||
|
||||
storeUserData(data) {
|
||||
this.setItem('userData', JSON.stringify(data));
|
||||
}
|
||||
|
||||
getUserData() {
|
||||
return JSON.parse(this.getItem('userData'));
|
||||
}
|
||||
|
||||
storeRememberData({token, userId, expiration}) {
|
||||
|
@ -73,6 +84,10 @@ class SessionStore {
|
|||
this.removeItem('rememberData-expiration');
|
||||
}
|
||||
|
||||
clearUserData() {
|
||||
this.removeItem('userData');
|
||||
}
|
||||
|
||||
getItem(key) {
|
||||
return this.storage.getItem(key);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ class SessionReducer extends Reducer {
|
|||
'LOGIN_FULFILLED': this.onLoginCompleted.bind(this),
|
||||
'LOGIN_REJECTED': this.onLoginFailed,
|
||||
'LOGOUT_FULFILLED': this.onLogout,
|
||||
'USER_DATA_FULFILLED': this.onUserDataRetrieved,
|
||||
'CHECK_SESSION_REJECTED': (state) => { return _.extend({}, state, {initDone: true})},
|
||||
'SESSION_CHECKED': (state) => { return _.extend({}, state, {initDone: true, logged: true})},
|
||||
'SESSION_CHECKED': this.onSessionChecked,
|
||||
'LOGIN_AUTO_FULFILLED': this.onAutoLogin.bind(this),
|
||||
'LOGIN_AUTO_REJECTED': this.onAutoLoginFail
|
||||
};
|
||||
|
@ -54,7 +55,6 @@ class SessionReducer extends Reducer {
|
|||
|
||||
onLogout(state) {
|
||||
sessionStore.closeSession();
|
||||
sessionStore.clearRememberData();
|
||||
|
||||
return _.extend({}, state, {
|
||||
initDone: true,
|
||||
|
@ -77,7 +77,6 @@ class SessionReducer extends Reducer {
|
|||
|
||||
onAutoLoginFail(state) {
|
||||
sessionStore.closeSession();
|
||||
sessionStore.clearRememberData();
|
||||
|
||||
return _.extend({}, state, {
|
||||
initDone: true
|
||||
|
@ -95,6 +94,30 @@ class SessionReducer extends Reducer {
|
|||
|
||||
sessionStore.createSession(resultData.userId, resultData.token);
|
||||
}
|
||||
|
||||
onUserDataRetrieved(state, payload) {
|
||||
let userData = payload.data;
|
||||
|
||||
sessionStore.storeUserData(payload.data);
|
||||
|
||||
return _.extend({}, state, {
|
||||
userName: userData.name,
|
||||
userEmail: userData.email,
|
||||
userTickets: userData.tickets
|
||||
});
|
||||
}
|
||||
|
||||
onSessionChecked(state) {
|
||||
let userData = sessionStore.getUserData();
|
||||
|
||||
return _.extend({}, state, {
|
||||
initDone: true,
|
||||
logged: true,
|
||||
userName: userData.name,
|
||||
userEmail: userData.email,
|
||||
userTickets: userData.tickets
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default SessionReducer.getInstance();
|
|
@ -1,4 +1,53 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=95955b93b458c15deb17e935e7374a2f)
|
||||
* Config saved to config.json and https://gist.github.com/95955b93b458c15deb17e935e7374a2f
|
||||
*/
|
||||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
|
@ -148,6 +197,78 @@ td,
|
|||
th {
|
||||
padding: 0;
|
||||
}
|
||||
/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
|
||||
@media print {
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
background: transparent !important;
|
||||
color: #000 !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
a,
|
||||
a:visited {
|
||||
text-decoration: underline;
|
||||
}
|
||||
a[href]:after {
|
||||
content: " (" attr(href) ")";
|
||||
}
|
||||
abbr[title]:after {
|
||||
content: " (" attr(title) ")";
|
||||
}
|
||||
a[href^="#"]:after,
|
||||
a[href^="javascript:"]:after {
|
||||
content: "";
|
||||
}
|
||||
pre,
|
||||
blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
tr,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
p,
|
||||
h2,
|
||||
h3 {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
h2,
|
||||
h3 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
.navbar {
|
||||
display: none;
|
||||
}
|
||||
.btn > .caret,
|
||||
.dropup > .btn > .caret {
|
||||
border-top-color: #000 !important;
|
||||
}
|
||||
.label {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
.table {
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
.table td,
|
||||
.table th {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.table-bordered th,
|
||||
.table-bordered td {
|
||||
border: 1px solid #ddd !important;
|
||||
}
|
||||
}
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
|
@ -188,7 +309,6 @@ a:focus {
|
|||
text-decoration: underline;
|
||||
}
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
@ -250,6 +370,416 @@ hr {
|
|||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
h1 small,
|
||||
h2 small,
|
||||
h3 small,
|
||||
h4 small,
|
||||
h5 small,
|
||||
h6 small,
|
||||
.h1 small,
|
||||
.h2 small,
|
||||
.h3 small,
|
||||
.h4 small,
|
||||
.h5 small,
|
||||
.h6 small,
|
||||
h1 .small,
|
||||
h2 .small,
|
||||
h3 .small,
|
||||
h4 .small,
|
||||
h5 .small,
|
||||
h6 .small,
|
||||
.h1 .small,
|
||||
.h2 .small,
|
||||
.h3 .small,
|
||||
.h4 .small,
|
||||
.h5 .small,
|
||||
.h6 .small {
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
color: #777777;
|
||||
}
|
||||
h1,
|
||||
.h1,
|
||||
h2,
|
||||
.h2,
|
||||
h3,
|
||||
.h3 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h1 small,
|
||||
.h1 small,
|
||||
h2 small,
|
||||
.h2 small,
|
||||
h3 small,
|
||||
.h3 small,
|
||||
h1 .small,
|
||||
.h1 .small,
|
||||
h2 .small,
|
||||
.h2 .small,
|
||||
h3 .small,
|
||||
.h3 .small {
|
||||
font-size: 65%;
|
||||
}
|
||||
h4,
|
||||
.h4,
|
||||
h5,
|
||||
.h5,
|
||||
h6,
|
||||
.h6 {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h4 small,
|
||||
.h4 small,
|
||||
h5 small,
|
||||
.h5 small,
|
||||
h6 small,
|
||||
.h6 small,
|
||||
h4 .small,
|
||||
.h4 .small,
|
||||
h5 .small,
|
||||
.h5 .small,
|
||||
h6 .small,
|
||||
.h6 .small {
|
||||
font-size: 75%;
|
||||
}
|
||||
h1,
|
||||
.h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
h2,
|
||||
.h2 {
|
||||
font-size: 30px;
|
||||
}
|
||||
h3,
|
||||
.h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h4,
|
||||
.h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
h5,
|
||||
.h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
h6,
|
||||
.h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.lead {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
line-height: 1.4;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.lead {
|
||||
font-size: 21px;
|
||||
}
|
||||
}
|
||||
small,
|
||||
.small {
|
||||
font-size: 85%;
|
||||
}
|
||||
mark,
|
||||
.mark {
|
||||
background-color: #fcf8e3;
|
||||
padding: .2em;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.text-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.text-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.text-lowercase {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.text-capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.text-muted {
|
||||
color: #777777;
|
||||
}
|
||||
.text-primary {
|
||||
color: #337ab7;
|
||||
}
|
||||
a.text-primary:hover,
|
||||
a.text-primary:focus {
|
||||
color: #286090;
|
||||
}
|
||||
.text-success {
|
||||
color: #3c763d;
|
||||
}
|
||||
a.text-success:hover,
|
||||
a.text-success:focus {
|
||||
color: #2b542c;
|
||||
}
|
||||
.text-info {
|
||||
color: #31708f;
|
||||
}
|
||||
a.text-info:hover,
|
||||
a.text-info:focus {
|
||||
color: #245269;
|
||||
}
|
||||
.text-warning {
|
||||
color: #8a6d3b;
|
||||
}
|
||||
a.text-warning:hover,
|
||||
a.text-warning:focus {
|
||||
color: #66512c;
|
||||
}
|
||||
.text-danger {
|
||||
color: #a94442;
|
||||
}
|
||||
a.text-danger:hover,
|
||||
a.text-danger:focus {
|
||||
color: #843534;
|
||||
}
|
||||
.bg-primary {
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
}
|
||||
a.bg-primary:hover,
|
||||
a.bg-primary:focus {
|
||||
background-color: #286090;
|
||||
}
|
||||
.bg-success {
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
a.bg-success:hover,
|
||||
a.bg-success:focus {
|
||||
background-color: #c1e2b3;
|
||||
}
|
||||
.bg-info {
|
||||
background-color: #d9edf7;
|
||||
}
|
||||
a.bg-info:hover,
|
||||
a.bg-info:focus {
|
||||
background-color: #afd9ee;
|
||||
}
|
||||
.bg-warning {
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
a.bg-warning:hover,
|
||||
a.bg-warning:focus {
|
||||
background-color: #f7ecb5;
|
||||
}
|
||||
.bg-danger {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
a.bg-danger:hover,
|
||||
a.bg-danger:focus {
|
||||
background-color: #e4b9b9;
|
||||
}
|
||||
.page-header {
|
||||
padding-bottom: 9px;
|
||||
margin: 40px 0 20px;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
}
|
||||
ul,
|
||||
ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol,
|
||||
ol ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.list-unstyled {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.list-inline {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
margin-left: -5px;
|
||||
}
|
||||
.list-inline > li {
|
||||
display: inline-block;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
dt,
|
||||
dd {
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.dl-horizontal dt {
|
||||
float: left;
|
||||
width: 160px;
|
||||
clear: left;
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dl-horizontal dd {
|
||||
margin-left: 180px;
|
||||
}
|
||||
}
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
cursor: help;
|
||||
border-bottom: 1px dotted #777777;
|
||||
}
|
||||
.initialism {
|
||||
font-size: 90%;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
blockquote {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 20px;
|
||||
font-size: 17.5px;
|
||||
border-left: 5px solid #eeeeee;
|
||||
}
|
||||
blockquote p:last-child,
|
||||
blockquote ul:last-child,
|
||||
blockquote ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
blockquote footer,
|
||||
blockquote small,
|
||||
blockquote .small {
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
line-height: 1.42857143;
|
||||
color: #777777;
|
||||
}
|
||||
blockquote footer:before,
|
||||
blockquote small:before,
|
||||
blockquote .small:before {
|
||||
content: '\2014 \00A0';
|
||||
}
|
||||
.blockquote-reverse,
|
||||
blockquote.pull-right {
|
||||
padding-right: 15px;
|
||||
padding-left: 0;
|
||||
border-right: 5px solid #eeeeee;
|
||||
border-left: 0;
|
||||
text-align: right;
|
||||
}
|
||||
.blockquote-reverse footer:before,
|
||||
blockquote.pull-right footer:before,
|
||||
.blockquote-reverse small:before,
|
||||
blockquote.pull-right small:before,
|
||||
.blockquote-reverse .small:before,
|
||||
blockquote.pull-right .small:before {
|
||||
content: '';
|
||||
}
|
||||
.blockquote-reverse footer:after,
|
||||
blockquote.pull-right footer:after,
|
||||
.blockquote-reverse small:after,
|
||||
blockquote.pull-right small:after,
|
||||
.blockquote-reverse .small:after,
|
||||
blockquote.pull-right .small:after {
|
||||
content: '\00A0 \2014';
|
||||
}
|
||||
address {
|
||||
margin-bottom: 20px;
|
||||
font-style: normal;
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
kbd {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #ffffff;
|
||||
background-color: #333333;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
pre {
|
||||
display: block;
|
||||
padding: 9.5px;
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
line-height: 1.42857143;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
color: #333333;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
pre code {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
white-space: pre-wrap;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
.pre-scrollable {
|
||||
max-height: 340px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.container {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
@ -917,8 +1447,250 @@ hr {
|
|||
margin-left: 0%;
|
||||
}
|
||||
}
|
||||
table {
|
||||
background-color: transparent;
|
||||
}
|
||||
caption {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: #777777;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
.table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table > thead > tr > th,
|
||||
.table > tbody > tr > th,
|
||||
.table > tfoot > tr > th,
|
||||
.table > thead > tr > td,
|
||||
.table > tbody > tr > td,
|
||||
.table > tfoot > tr > td {
|
||||
padding: 8px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: top;
|
||||
//border-top: 1px solid #dddddd;
|
||||
}
|
||||
.table > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
//border-bottom: 2px solid #dddddd;
|
||||
}
|
||||
.table > caption + thead > tr:first-child > th,
|
||||
.table > colgroup + thead > tr:first-child > th,
|
||||
.table > thead:first-child > tr:first-child > th,
|
||||
.table > caption + thead > tr:first-child > td,
|
||||
.table > colgroup + thead > tr:first-child > td,
|
||||
.table > thead:first-child > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
.table > tbody + tbody {
|
||||
//border-top: 2px solid #dddddd;
|
||||
}
|
||||
.table .table {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.table-condensed > thead > tr > th,
|
||||
.table-condensed > tbody > tr > th,
|
||||
.table-condensed > tfoot > tr > th,
|
||||
.table-condensed > thead > tr > td,
|
||||
.table-condensed > tbody > tr > td,
|
||||
.table-condensed > tfoot > tr > td {
|
||||
padding: 5px;
|
||||
}
|
||||
.table-bordered {
|
||||
//border: 1px solid #dddddd;
|
||||
}
|
||||
.table-bordered > thead > tr > th,
|
||||
.table-bordered > tbody > tr > th,
|
||||
.table-bordered > tfoot > tr > th,
|
||||
.table-bordered > thead > tr > td,
|
||||
.table-bordered > tbody > tr > td,
|
||||
.table-bordered > tfoot > tr > td {
|
||||
//border: 1px solid #dddddd;
|
||||
}
|
||||
.table-bordered > thead > tr > th,
|
||||
.table-bordered > thead > tr > td {
|
||||
//border-bottom-width: 2px;
|
||||
}
|
||||
.table-striped > tbody > tr:nth-of-type(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.table-hover > tbody > tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
table col[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-column;
|
||||
}
|
||||
table td[class*="col-"],
|
||||
table th[class*="col-"] {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
.table > thead > tr > td.active,
|
||||
.table > tbody > tr > td.active,
|
||||
.table > tfoot > tr > td.active,
|
||||
.table > thead > tr > th.active,
|
||||
.table > tbody > tr > th.active,
|
||||
.table > tfoot > tr > th.active,
|
||||
.table > thead > tr.active > td,
|
||||
.table > tbody > tr.active > td,
|
||||
.table > tfoot > tr.active > td,
|
||||
.table > thead > tr.active > th,
|
||||
.table > tbody > tr.active > th,
|
||||
.table > tfoot > tr.active > th {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.table-hover > tbody > tr > td.active:hover,
|
||||
.table-hover > tbody > tr > th.active:hover,
|
||||
.table-hover > tbody > tr.active:hover > td,
|
||||
.table-hover > tbody > tr:hover > .active,
|
||||
.table-hover > tbody > tr.active:hover > th {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
.table > thead > tr > td.success,
|
||||
.table > tbody > tr > td.success,
|
||||
.table > tfoot > tr > td.success,
|
||||
.table > thead > tr > th.success,
|
||||
.table > tbody > tr > th.success,
|
||||
.table > tfoot > tr > th.success,
|
||||
.table > thead > tr.success > td,
|
||||
.table > tbody > tr.success > td,
|
||||
.table > tfoot > tr.success > td,
|
||||
.table > thead > tr.success > th,
|
||||
.table > tbody > tr.success > th,
|
||||
.table > tfoot > tr.success > th {
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
.table-hover > tbody > tr > td.success:hover,
|
||||
.table-hover > tbody > tr > th.success:hover,
|
||||
.table-hover > tbody > tr.success:hover > td,
|
||||
.table-hover > tbody > tr:hover > .success,
|
||||
.table-hover > tbody > tr.success:hover > th {
|
||||
background-color: #d0e9c6;
|
||||
}
|
||||
.table > thead > tr > td.info,
|
||||
.table > tbody > tr > td.info,
|
||||
.table > tfoot > tr > td.info,
|
||||
.table > thead > tr > th.info,
|
||||
.table > tbody > tr > th.info,
|
||||
.table > tfoot > tr > th.info,
|
||||
.table > thead > tr.info > td,
|
||||
.table > tbody > tr.info > td,
|
||||
.table > tfoot > tr.info > td,
|
||||
.table > thead > tr.info > th,
|
||||
.table > tbody > tr.info > th,
|
||||
.table > tfoot > tr.info > th {
|
||||
background-color: #d9edf7;
|
||||
}
|
||||
.table-hover > tbody > tr > td.info:hover,
|
||||
.table-hover > tbody > tr > th.info:hover,
|
||||
.table-hover > tbody > tr.info:hover > td,
|
||||
.table-hover > tbody > tr:hover > .info,
|
||||
.table-hover > tbody > tr.info:hover > th {
|
||||
background-color: #c4e3f3;
|
||||
}
|
||||
.table > thead > tr > td.warning,
|
||||
.table > tbody > tr > td.warning,
|
||||
.table > tfoot > tr > td.warning,
|
||||
.table > thead > tr > th.warning,
|
||||
.table > tbody > tr > th.warning,
|
||||
.table > tfoot > tr > th.warning,
|
||||
.table > thead > tr.warning > td,
|
||||
.table > tbody > tr.warning > td,
|
||||
.table > tfoot > tr.warning > td,
|
||||
.table > thead > tr.warning > th,
|
||||
.table > tbody > tr.warning > th,
|
||||
.table > tfoot > tr.warning > th {
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
.table-hover > tbody > tr > td.warning:hover,
|
||||
.table-hover > tbody > tr > th.warning:hover,
|
||||
.table-hover > tbody > tr.warning:hover > td,
|
||||
.table-hover > tbody > tr:hover > .warning,
|
||||
.table-hover > tbody > tr.warning:hover > th {
|
||||
background-color: #faf2cc;
|
||||
}
|
||||
.table > thead > tr > td.danger,
|
||||
.table > tbody > tr > td.danger,
|
||||
.table > tfoot > tr > td.danger,
|
||||
.table > thead > tr > th.danger,
|
||||
.table > tbody > tr > th.danger,
|
||||
.table > tfoot > tr > th.danger,
|
||||
.table > thead > tr.danger > td,
|
||||
.table > tbody > tr.danger > td,
|
||||
.table > tfoot > tr.danger > td,
|
||||
.table > thead > tr.danger > th,
|
||||
.table > tbody > tr.danger > th,
|
||||
.table > tfoot > tr.danger > th {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
.table-hover > tbody > tr > td.danger:hover,
|
||||
.table-hover > tbody > tr > th.danger:hover,
|
||||
.table-hover > tbody > tr.danger:hover > td,
|
||||
.table-hover > tbody > tr:hover > .danger,
|
||||
.table-hover > tbody > tr.danger:hover > th {
|
||||
background-color: #ebcccc;
|
||||
}
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
min-height: 0.01%;
|
||||
}
|
||||
@media screen and (max-width: 767px) {
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
overflow-y: hidden;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
//border: 1px solid #dddddd;
|
||||
}
|
||||
.table-responsive > .table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table-responsive > .table > thead > tr > th,
|
||||
.table-responsive > .table > tbody > tr > th,
|
||||
.table-responsive > .table > tfoot > tr > th,
|
||||
.table-responsive > .table > thead > tr > td,
|
||||
.table-responsive > .table > tbody > tr > td,
|
||||
.table-responsive > .table > tfoot > tr > td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table-responsive > .table-bordered {
|
||||
border: 0;
|
||||
}
|
||||
.table-responsive > .table-bordered > thead > tr > th:first-child,
|
||||
.table-responsive > .table-bordered > tbody > tr > th:first-child,
|
||||
.table-responsive > .table-bordered > tfoot > tr > th:first-child,
|
||||
.table-responsive > .table-bordered > thead > tr > td:first-child,
|
||||
.table-responsive > .table-bordered > tbody > tr > td:first-child,
|
||||
.table-responsive > .table-bordered > tfoot > tr > td:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
.table-responsive > .table-bordered > thead > tr > th:last-child,
|
||||
.table-responsive > .table-bordered > tbody > tr > th:last-child,
|
||||
.table-responsive > .table-bordered > tfoot > tr > th:last-child,
|
||||
.table-responsive > .table-bordered > thead > tr > td:last-child,
|
||||
.table-responsive > .table-bordered > tbody > tr > td:last-child,
|
||||
.table-responsive > .table-bordered > tfoot > tr > td:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
.table-responsive > .table-bordered > tbody > tr:last-child > th,
|
||||
.table-responsive > .table-bordered > tfoot > tr:last-child > th,
|
||||
.table-responsive > .table-bordered > tbody > tr:last-child > td,
|
||||
.table-responsive > .table-bordered > tfoot > tr:last-child > td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
.clearfix:before,
|
||||
.clearfix:after,
|
||||
.dl-horizontal dd:before,
|
||||
.dl-horizontal dd:after,
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
|
@ -929,6 +1701,7 @@ hr {
|
|||
display: table;
|
||||
}
|
||||
.clearfix:after,
|
||||
.dl-horizontal dd:after,
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after {
|
||||
|
|
Loading…
Reference in New Issue