Commit cd769d4e authored by Juorder Gonzalez's avatar Juorder Gonzalez

Merge pull request #16 from ZBoxApp/domains

Domain view and some other examples
parents baaf0ce4 6f5c0d11
......@@ -16,3 +16,10 @@ export function emitEndLoading() {
type: ActionTypes.END_LOADING
});
}
export function saveUser(user) {
AppDispatcher.handleViewAction({
type: ActionTypes.USER_CHANGED,
user
});
}
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import $ from 'jquery';
import React from 'react';
import {browserHistory} from 'react-router';
import MessageBar from '../message_bar.jsx';
import PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
export default class Domains extends React.Component {
constructor(props) {
super(props);
this.handleLink = this.handleLink.bind(this);
this.getDomains = this.getDomains.bind(this);
this.state = {};
}
handleLink(e, path) {
e.preventDefault();
if (`/${this.props.location.pathname}` !== path) {
GlobalActions.emitStartLoading();
browserHistory.push(path);
}
}
getDomains() {
Client.getAllDomains(
(data) => {
this.setState({
data
});
GlobalActions.emitEndLoading();
},
(error) => {
this.setState({
error: error.message
});
GlobalActions.emitEndLoading();
}
);
}
componentDidMount() {
$('#sidebar-domains').addClass('active');
this.getDomains();
}
componentWillUnmount() {
$('#sidebar-domains').removeClass('active');
}
render() {
let message;
if (this.state.error) {
message = (
<MessageBar
message={this.state.error}
type='success'
autoclose={true}
/>
);
}
const addDomainButton = [{
label: 'Agregar Dominio',
props: {
className: 'btn btn-success',
onClick: (e) => {
this.handleLink(e, '/domains/new');
}
}
}];
let tableResults;
if (this.state.data) {
tableResults = this.state.data.map((d) => {
let status;
let statusClass = 'btn btn-sm ';
switch (d.attrs.zimbraDomainStatus) {
case 'active':
status = 'Activo';
statusClass += 'btn-info';
break;
case 'inactive':
status = 'Inactivo';
statusClass += 'btn-default';
break;
default:
status = 'Migrando';
statusClass += 'btn-warning2';
break;
}
let mailboxes;
if (d.mailboxes) {
const types = d.mailboxes.types.map((t, i) => {
return (<li key={`mailbox-${d.id}-${i}`}>{t.count} {t.type}</li>);
});
mailboxes = (
<td className='vertical-middle text-center'>
<span className='total-mbxs-per-domain'>d.mailboxes.total</span>
<ul className='list-inline'>
{types}
</ul>
</td>
);
} else {
mailboxes = (<td className='vertical-middle text-center'/>);
}
return (
<tr
key={d.id}
className='domain-row'
id={`domain-${d.id}`}
>
<td className='domain-name'>
<h4>
<a
href='#'
onClick={(e) => this.handleLink(e, '/domains/' + d.id)}
>
{d.name}
</a>
<br/>
<small/>
</h4>
</td>
{mailboxes}
<td className='vertical-middle text-justify'>
{d.attrs.description}
</td>
<td className='vertical-middle text-center'>
<StatusLabel
classes={statusClass}
children={status}
/>
</td>
</tr>
);
});
}
const panelBody = (
<div>
<div
id='index-domains-table'
className='table-responsive'
>
<table
id='index-domains'
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align'
>
<thead>
<tr>
<th>{'Nombre'}</th>
<th className='td-mbxs'>{'Casillas Usadas'}</th>
<th className='text-center'>{'Descripción'}</th>
<th className='text-center'>{'Estado'}</th>
</tr>
</thead>
<tbody>
{tableResults}
</tbody>
</table>
</div>
</div>
);
return (
<div>
<PageInfo
titlePage='Dominios'
descriptionPage='Dominios de correos creados'
/>
{message}
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
btnsHeader={addDomainButton}
children={panelBody}
/>
</div>
</div>
</div>
</div>
);
}
}
Domains.propTypes = {
location: React.PropTypes.object.isRequired
};
......@@ -25,7 +25,7 @@ export default class Header extends React.Component {
browserHistory.push(`search/global?utf8=${utf8}&query=${encodeURIComponent(term)}`);
}
toggleSidebar() {
$('body').toggleClass('hide-sidebar');
$('body').toggleClass('hide-sidebar').toggleClass('show-sidebar');
}
render() {
return (
......
......@@ -14,7 +14,7 @@ export default class LoggedIn extends React.Component {
<LoadingScreen/>
<Header/>
<Sidebar location={this.props.location}/>
<div className='wrapper'>
<div id='wrapper'>
{this.props.children}
</div>
</div>
......
......@@ -66,6 +66,10 @@ export default class MessageBar extends React.Component {
case 'error':
icon = (<i className='fa fa fa-exclamation-circle'></i>);
break;
case 'success':
break;
case 'warning':
break;
}
return (
......
......@@ -3,7 +3,7 @@ import React from 'react';
export default class PageInfo extends React.Component {
render() {
return (
<div className='normalheader transition animated fadeIn small-header'>
<div className='transition animated fadeIn small-header'>
<div className='hpanel'>
<div className='panel-body'>
<h2 className='font-light m-b-xs'>
......
import React from 'react';
export default class PanelForm extends React.Component {
render() {
return (
<form {...this.props.formAttrs}>
<input
type='hidden'
name='utf8'
value='✓'
/>
<div className='input-group'>
<input {...this.props.hiddenAttrs}/>
<input {...this.props.inputAttrs}/>
<span className='input-group-btn'>
<button
className='btn btn-default'
type='submit'
>
<span className={this.props.iconClasses}></span>
</button>
</span>
</div>
</form>
);
}
}
PanelForm.propTypes = {
formAttrs: React.PropTypes.object,
hiddenAttrs: React.PropTypes.object,
inputAttrs: React.PropTypes.object,
iconClasses: React.PropTypes.string
};
PanelForm.defaultProps = {
iconClasses: 'fa fa-search',
formAttrs: {name: 'search_domain'},
hiddenAttrs: {type: 'hidden'},
inputAttrs: {}
};
import React from 'react';
import Button from './button.jsx';
import PanelForm from './panelForm.jsx';
import Pagination from './pagination.jsx';
import Alert from './alert.jsx';
export default class PanelTable extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [
{name: 'zbox', casillas: '2', descripcion: 'Caracas', estado: 'active', props: {className: 'domain-row'}},
{name: 'Nombre', casillas: '2NNS', descripcion: 'Descripción', estado: 'active', props: {className: 'domain-row'}},
{name: 'microsoft', casillas: '34NS', descripcion: 'Solteros', estado: 'active', props: {className: 'domain-row'}},
{name: 'descargasnsn', casillas: '2NNS', descripcion: 'Descripción', estado: 'migrando', props: {className: 'domain-row'}},
{name: 'Nombre', casillas: '2NNS', descripcion: 'Descripción', estado: 'active', props: {className: 'domain-row'}},
{name: 'machinesoft', casillas: '', descripcion: 'Hola que tal', estado: 'inactive', props: {className: 'domain-row'}},
{name: 'Nombre', casillas: '2S', descripcion: 'Descripción', estado: 'active', props: {className: 'domain-row'}},
{name: 'getonboard', casillas: '45NS', descripcion: 'Casados', estado: 'inactive', props: {className: 'domain-row'}}
],
pagination: [
{url: './domains/1', label: '1', active: 'active'},
{url: './domains/2', label: '2'},
{url: './domains/3', label: '3'},
{url: './domains/prev', label: 'prev'},
{url: './domains/next', label: 'next'}
]
};
}
render() {
const btns = this.props.btnsHeader.map((btn, i) => {
return (
......@@ -45,7 +19,7 @@ export default class PanelTable extends React.Component {
let content;
if (this.state.data.length <= 0) {
content = <div className='empty-search'>Sin resultados para tu búsqueda</div>;
content = <div className='empty-search'>{'Sin resultados para tu búsqueda'}</div>;
} else {
const rows = this.state.data.map((row, i) => {
return (
......@@ -112,10 +86,6 @@ export default class PanelTable extends React.Component {
<div className='panel-heading hbuilt clearfix'>
<div className='pull-right'>{btns}</div>
<div className='panel-header-search'>
<PanelForm
formAttrs={{name: 'search_name', 'data-remote': true}}
inputAttrs={{type: 'text', name: 'search[query]', id: 'search_query', className: 'form-control', placeholder: 'Buscar Dominio Por Nombre'}}
/>
</div>
</div>
<div className='panel-body'>
......
......@@ -11,17 +11,21 @@ export default class Sidebar extends React.Component {
constructor(props) {
super(props);
this.state = this.getStateFromStores();
}
getStateFromStores() {
const user = UserStore.getCurrentUser();
// if user is null then get the user from Zimbra using the token
this.onUserChanged = this.onUserChanged.bind(this);
return {
user
this.state = {
user: UserStore.getCurrentUser()
};
}
componentDidMount() {
UserStore.addChangeListener(this.onUserChanged);
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onUserChanged);
}
onUserChanged() {
this.setState({user: UserStore.getCurrentUser()});
}
render() {
if (this.state.user) {
return (
......@@ -33,7 +37,7 @@ export default class Sidebar extends React.Component {
<Link
to={`/mailboxes/${this.state.user.id}`}
>
{this.state.user.email}
{this.state.user.name}
</Link>
<small className='text-muted'></small>
</span>
......
......@@ -14,7 +14,7 @@ export default class SidebarMenu extends React.Component {
handleLink(e, path) {
e.preventDefault();
if (`/${this.props.location.pathname}` !== path) {
GlobalActions.emitEndLoading();
GlobalActions.emitStartLoading();
browserHistory.push(path);
}
}
......@@ -40,7 +40,7 @@ export default class SidebarMenu extends React.Component {
<span className='nav-label'>{'cuentas'}</span>
</a>
</li>
<li ref='sidebar-domains'>
<li id='sidebar-domains'>
<a
href='#'
onClick={(e) => this.handleLink(e, '/domains')}
......@@ -48,7 +48,7 @@ export default class SidebarMenu extends React.Component {
<span className='nav-label'>{'dominios'}</span>
</a>
</li>
<li ref='sidebar-mailboxes'>
<li id='sidebar-mailboxes'>
<a
href='#'
onClick={(e) => this.handleLink(e, '/mailboxes')}
......
......@@ -47,7 +47,7 @@
}
</script>
</head>
<body>
<body class='show-sidebar'>
<div id='root'/>
<script>
window.setup_root();
......
......@@ -3,12 +3,15 @@
import './sass/styles.scss';
import UserStore from './stores/user_store.jsx';
import Root from './components/root.jsx';
import ErrorPage from './components/error_page.jsx';
import LoggedIn from './components/logged_in.jsx';
import NotLoggedIn from './components/not_logged_in.jsx';
import Login from './components/login/login.jsx';
import Accounts from './components/accounts/accounts.jsx';
import Domains from './components/domain/domains.jsx';
import * as Client from './utils/client.jsx';
import * as Utils from './utils/utils.jsx';
......@@ -52,7 +55,18 @@ function onPreLoggedIn(nextState, replace, callback) {
if (!data || !data.logged_in) {
return browserHistory.push('/login');
}
return callback();
if (UserStore.getCurrentUser()) {
return callback();
}
return Client.getMe(
() => {
return callback();
},
() => {
return browserHistory.push('/login');
});
});
}
......@@ -87,6 +101,10 @@ function renderRootComponent() {
path='accounts'
component={Accounts}
/>
<Route
path='domains'
component={Domains}
/>
<Route
path='search/global'
/>
......
......@@ -541,7 +541,7 @@ body {
}
a {
color: $color-navy-blue;
color: $primary-color;
cursor: pointer;
&.list-group-item.active {
......@@ -1221,8 +1221,16 @@ h6 {
// Tables
.table > thead > tr > th {
border-bottom: 0;
.table {
> thead > tr > th {
border-bottom: 0;
}
td {
&.vertical-middle {
vertical-align: middle;
}
}
}
// Validation
......
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
class UserStoreClass {
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import EventEmitter from 'events';
import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
const CHANGE_EVENT = 'change';
class UserStoreClass extends EventEmitter {
constructor() {
this.currentUser = {};
super();
this.currentUser = null;
}
getCurrentUser() {
......@@ -23,8 +31,33 @@ class UserStoreClass {
return null;
}
emitChange() {
this.emit(CHANGE_EVENT);
}
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
}
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
}
var UserStore = new UserStoreClass();
const UserStore = new UserStoreClass();
UserStore.setMaxListeners(0);
UserStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action;
switch (action.type) {
case ActionTypes.USER_CHANGED:
UserStore.setCurrentUser(action.user);
UserStore.emitChange();
break;
default:
}
});
export {UserStore as default};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
class ZimbraStoreClass {
constructor() {
this.zimbra = null;
}
getCurrent() {
return this.zimbra;
}
setCurrent(zimbra) {
this.zimbra = zimbra;
}
}
var ZimbraStore = new ZimbraStoreClass();
export {ZimbraStore as default};
......@@ -2,16 +2,24 @@
// See LICENSE.txt for license information.
import $ from 'jquery';
import jszimbra from 'js-zimbra';
import UserStore from '../stores/user_store.jsx';
import * as Utils from './utils.jsx';
import {browserHistory} from 'react-router';
import ZimbraAdminApi from 'zimbra-admin-api-js';
import ZimbraStore from '../stores/zimbra_store.jsx';
// import Domain from '../zimbra/domain.jsx';
import * as GlobalActions from '../action_creators/global_actions.jsx';
import * as Utils from './utils.jsx';
import Constants from './constants.jsx';
let domain;
// arguments.callee.name
// función que maneja el error como corresponde
function handleError(methodName, err) {
if (err.type && err.type === Constants.ActionTypes.NOT_LOGGED_IN) {
browserHistory.push('/login');
return err;
}
let e = null;
try {
e = JSON.parse(err.responseText);
......@@ -33,6 +41,31 @@ function handleError(methodName, err) {
return error;
}
function initZimbra() {
return new Promise(
(resolve, reject) => {
const config = global.window.manager_config;
const token = Utils.getCookie('token');
let zimbra = ZimbraStore.getCurrent();
if (zimbra && token) {
return resolve(zimbra);
} else if (token) {
zimbra = new ZimbraAdminApi({
url: config.zimbraUrl
});
zimbra.client.token = token;
ZimbraStore.setCurrent(zimbra);
return resolve(zimbra);
}
return reject({
type: Constants.ActionTypes.NOT_LOGGED_IN,
message: 'No instanciado'
});
});
}
export function getClientConfig(success, error) {
return $.ajax({
url: '/config/config.json',
......@@ -45,41 +78,55 @@ export function getClientConfig(success, error) {
});
}
export function login(username, secret, success, error) {
export function getMe(success, error) {
initZimbra().then(
(zimbra) => {
zimbra.getInfo((err, data) => {
if (err) {
let e = handleError('getMe', err);
return error(e);
}
Reflect.deleteProperty(data, 'obj');
GlobalActions.saveUser(data);
return success();
});
},
(err) => {
let e = handleError('getMe', err);
return error(e);
}
);
}
export function login(user, password, success, error) {
const config = global.window.manager_config;
const zimbra = new jszimbra.Communication({
const zimbra = new ZimbraAdminApi({
url: config.zimbraUrl,
debug: config.debug
user,
password
});
zimbra.auth({
username,
secret,
isPassword: true,
isAdmin: true
}, (err) => {
zimbra.client.debug = config.debug;
zimbra.login((err) => {
if (err) {
var e = handleError('login', err);
return error(e);
}
Utils.setCookie('token', zimbra.token, 3);
UserStore.setCurrentUser({
token: zimbra.token,
email: username
});
return success(zimbra);
Utils.setCookie('token', zimbra.client.token, 3);
ZimbraStore.setCurrent(zimbra);
return getMe(success, error);
});
}
export function logout(callback) {
// Aqui debemos asignar como null todas aquellas clases instanciadas
domain = null;
const cookie = Utils.getCookie('token');
if (cookie) {
Utils.setCookie('token', '', -1);
}
UserStore.setCurrentUser(null);
GlobalActions.saveUser(null);
return callback();
}
......@@ -99,18 +146,21 @@ export function isLoggedIn(callback) {
return callback(data);
}
export function getDomain(name, by, attrs, success, error) {
if (domain) {
return domain.get(name, by, attrs,
(data) => {
export function getAllDomains(success, error) {
initZimbra().then(
(zimbra) => {
zimbra.getAllDomains((err, data) => {
if (err) {
let e = handleError('getAllDomains', err);
return error(e);
}
return success(data);
},
(err) => {
var e = handleError('getDomain', err);
error(e);
});
}
// probablemente esto lo que deba hacer es forzar un login
return error({message: 'Domain not initialized'});
},
(err) => {
let e = handleError('getAllDomains', err);
return error(e);
}
);
}
......@@ -7,7 +7,9 @@ export default {
ActionTypes: keyMirror({
START_LOADING: null,
END_LOADING: null,
RECEIVED_ERROR: null
USER_CHANGED: null,
RECEIVED_ERROR: null,
NOT_LOGGED_IN: null
}),
PayloadSources: keyMirror({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment