Commit 68c90542 authored by Elias Nahum's avatar Elias Nahum

Domain details and tab component

parent 7e065f40
......@@ -23,6 +23,8 @@ const mimes = {
const proxy = httpProxy.createProxyServer({});
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
const zimbraProxy = process.env.zimbra || config.zimbraProxy; //eslint-disable-line no-process-env
if (req.url.indexOf('/service') === 0) {
......
// 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 PanelTab from '../panel_tab.jsx';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
export default class DomainDetails extends React.Component {
constructor(props) {
super(props);
this.handleLink = this.handleLink.bind(this);
this.getDomain = this.getDomain.bind(this);
this.state = {};
}
handleLink(e, path) {
e.preventDefault();
if (`/${this.props.location.pathname}` !== path) {
GlobalActions.emitStartLoading();
browserHistory.push(path);
}
}
getDomain() {
const domain = DomainStore.getCurrent();
if (domain) {
GlobalActions.emitEndLoading();
this.setState({
domain
});
} else {
Client.getDomain(
this.props.params.id,
(data) => {
this.setState({
domain: data
});
GlobalActions.emitEndLoading();
},
(error) => {
this.setState({
error: error.message
});
GlobalActions.emitEndLoading();
}
);
}
}
componentDidMount() {
$('#sidebar-domains').addClass('active');
this.getDomain();
}
componentWillUnmount() {
$('#sidebar-domains').removeClass('active');
}
render() {
const domain = this.state.domain;
if (domain) {
let message;
if (this.state.msg) {
message = (
<MessageBar
message={this.state.msg}
type='success'
autoclose={true}
/>
);
}
const casillasSinPlan = (
<div
id='mbxs-without-cos'
className='alert alert-danger'
role='alert'
>
<i className='fa fa-exclamation-circle'></i>
{'Existen casillas sin Clase de Servicio: 119'}
</div>
);
const editDomainButton = [{
label: 'Editar',
props: {
className: 'btn btn-default btn-xs',
onClick: (e) => this.handleLink(e, `/domains/${this.props.params.id}/edit`)
}
}];
const infoBody = (
<div className='row'>
<div className='col-md-12'>
<div
id={`domain-${domain.id}`}
className='domain-info'
>
<h4>
<span className='domain-name'>{`@${domain.name}`}</span>
<br/>
<small/>
</h4>
<p>
<a
className='account-name'
onClick={(e) => this.handleLink(e, `/accounts/${domain.id_empresa}`)}
>
{'Nombre de la Empresa'}
</a>
</p>
<ul className='list-unstyled'>
<li>
<strong>{'Now! Team: '}</strong>
{domain.attrs.postOfficeBox}
</li>
<li>
<strong>{'MX Record: '}</strong>
{'your-dns-needs-immediate-attention.dev'}
</li>
<li>
<strong>{'Próxima renovación: '}</strong>
{'19/10/2016'}
</li>
<li>
</li>
</ul>
<ul className='list-inline list-unstyled'>
<li>
<StatusLabel
classes='btn btn-md btn-info'
children='Anual'
/>
</li>
</ul>
</div>
</div>
</div>
);
const casillasBtns = [
{
label: 'Ver casillas',
props: {
className: 'btn btn-default btn-xs',
onClick: (e) => this.handleLink(e, `/domains/${this.props.params.id}/mailboxes`)
}
},
{
label: 'Nueva Casilla',
props: {
className: 'btn btn-info add-button btn-xs',
onClick: (e) => this.handleLink(e, `/domains/${this.props.params.id}/mailboxes/new`)
}
}
];
const casillasRows = [];
const casillasArray = [];
casillasArray.map((c) => {
return casillasRows.push(
<tr>
<td className='mbx-plan'
style={{borderTop: 0}}
>
{c.type}
</td>
<td style={{borderTop: 0}}>{c.limit}</td>
<td style={{borderTop: 0}}>{c.used}</td>
</tr>
);
});
const casillasBody = (
<table
id='domain-mbxs'
cellPadding='1'
cellSpacing='1'
className='table'
style={{marginBottom: '0px'}}
>
<thead>
<tr>
<th style={{width: '50%'}}></th>
<th>Límite</th>
<th>Usadas</th>
</tr>
</thead>
<tbody>
{casillasRows}
</tbody>
</table>
);
const tab1 = (
<Panel
title='Información General'
btnsHeader={editDomainButton}
children={infoBody}
/>
);
const tab2 = (
<Panel
title='Casillas'
btnsHeader={casillasBtns}
children={casillasBody}
/>
);
const panelTabs = (
<PanelTab
tabNames={['Administradores', 'Listas De Distribución']}
tabs={{
administradores: tab1,
listas_de_distribución: tab2
}}
/>
);
return (
<div>
<PageInfo
titlePage='Dominios'
descriptionPage='Dominios de correos creados'
/>
{message}
<div className='content animate-panel'>
{casillasSinPlan}
<div className='row'>
<div className='col-md-6 central-content'>
<Panel
title='Información General'
btnsHeader={editDomainButton}
children={infoBody}
/>
</div>
<div className='col-md-6 central-content'>
<Panel
title='Casillas'
btnsHeader={casillasBtns}
children={casillasBody}
/>
</div>
</div>
<div className='row'>
<div className='col-md-12 panel-with-tabs'>
{panelTabs}
</div>
</div>
</div>
</div>
);
}
return <div/>;
}
}
DomainDetails.propTypes = {
location: React.PropTypes.object.isRequired,
params: React.PropTypes.object.isRequired
};
......@@ -11,6 +11,8 @@ import Pagination from '../pagination.jsx';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
......@@ -31,8 +33,11 @@ export default class Domains extends React.Component {
offset: ((page - 1) * QueryOptions.DEFAULT_LIMIT)
};
}
handleLink(e, path) {
handleLink(e, domain) {
e.preventDefault();
DomainStore.setCurrent(domain);
const path = `/domains/${domain.id}`;
if (`/${this.props.location.pathname}` !== path) {
GlobalActions.emitStartLoading();
browserHistory.push(path);
......@@ -150,7 +155,7 @@ export default class Domains extends React.Component {
<h4>
<a
href='#'
onClick={(e) => this.handleLink(e, '/domains/' + d.id)}
onClick={(e) => this.handleLink(e, d)}
>
{d.name}
</a>
......
......@@ -17,7 +17,7 @@ export default class Panel extends React.Component {
let panelHeader;
if (this.props.hasHeader) {
panelHeader = (
<div className='panel-heading hbuilt clearfix'>
<div className='panel-heading hbuilt'>
<div className='pull-right'>{btns}</div>
<div className='heading-buttons'>
{this.props.title}
......
import React from 'react';
import Button from './button.jsx';
import Pagination from './pagination.jsx';
import Alert from './alert.jsx';
export default class PanelTable extends React.Component {
render() {
const btns = this.props.btnsHeader.map((btn, i) => {
return (
<Button
btnAttrs={btn.props}
key={i}
>
{btn.label}
</Button>
);
});
let content;
if (this.state.data.length <= 0) {
content = <div className='empty-search'>{'Sin resultados para tu búsqueda'}</div>;
} else {
const rows = this.state.data.map((row, i) => {
return (
<tr
key={i}
{...row.props}
>
<td>
{row.name}
</td>
<td>
{row.casillas}
</td>
<td>
{row.descripcion}
</td>
<td>
<span className={'label-fat label-domain-status-' + row.estado}>
{row.estado}
</span>
</td>
</tr>
);
});
content = (
<div>
<div
id='index-domains-table'
className='table-responsive'
>
<table className='table table-condensed table-striped vertical-align'>
<thead>
<tr>
<th>Nombre</th>
<th>Casillas Usadas</th>
<th>Descripción</th>
<th>Estado</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
<Pagination linksArray={this.state.pagination}/>
</div>
);
}
return (
<div>
<Alert
className='alert alert-success'
withClose={true}
>
<a href='./goTo'>Tu datos han sido guardados!.</a>
</Alert>
<div className='hpanel'>
<div className='panel-heading hbuilt clearfix'>
<div className='pull-right'>{btns}</div>
<div className='panel-header-search'>
</div>
</div>
<div className='panel-body'>
{content}
</div>
</div>
</div>
);
}
}
PanelTable.propTypes = {
btnsHeader: React.PropTypes.array
};
PanelTable.defaultProps = {
btnsHeader: []
};
import React from 'react';
import Button from './button.jsx';
import PanelForm from './panelForm.jsx';
import Pagination from './pagination.jsx';
import Anchor from './anchor.jsx';
export default class Panel extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: []
};
}
render() {
const btns = this.props.btnsHeader.map((btn, i) => {
return (
<Button
btnAttrs={btn.props}
key={i}
>
{btn.label}
</Button>
);
});
let content;
if (this.state.data.length <= 0) {
content = <div className='empty-search'>Sin resultados para tu búsqueda</div>;
} else {
const rows = this.state.data.map((row, i) => {
return (
<tr
key={i}
{...row.props}
>
<td>
{row.email}
</td>
<td>
{row.name}
</td>
<td>
{row.type}
</td>
<td>
{row.actions.map((btn, j) => {
return (
<Button
key={j}
btnAttrs={btn.props}
>
{btn.label}
</Button>
);
})}
</td>
</tr>
);
});
content = (
<div>
<div
id='index-domains-table'
className='table-responsive'
>
<table className='table table-condensed table-striped vertical-align'>
<thead>
<tr>
<th>Email</th>
<th>Nombre</th>
<th>Tipo</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
);
}
const tab = this.state.tabs.map((li, i) => {
return (
<Anchor
key={i}
attrs={li.props}
url={li.url}
label={li.label}
/>
);
});
let tabs = (
<ul className='nav nav-tabs'>
{tab}
</ul>
);
return (
<div
id='index-mailboxes'
className='col-md-12 panel-width-tabs mailboxes-index'
data-domain=''
>
<div className='hpanel'>
{tabs}
<div className='tab-content'>
<div
id='tab-index-mailboxes'
className='tab-pane active'
>
<div className='panel-body'>
<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>
{content}
<Pagination linksArray={this.state.pagination}/>
</div>
</div>
</div>
</div>
</div>
);
}
}
Panel.propTypes = {
btnsHeader: React.PropTypes.array
};
Panel.defaultProps = {
btnsHeader: []
};
import React from 'react';
import * as Utils from '../utils/utils.jsx';
export default class Panel extends React.Component {
constructor(props) {
super(props);
this.changeTab = this.changeTab.bind(this);
this.state = {
tab: Object.keys(this.props.tabs)[0]
};
}
changeTab(e, tabName) {
e.preventDefault();
this.setState({tab: Utils.slug(tabName)});
}
render() {
if (this.state.tab) {
const tabs = this.props.tabNames.map((t) => {
const slug = Utils.slug(t);
return (
<li
key={slug}
className={slug === this.state.tab ? 'active' : ''}
>
<a
data-toggle='tab'
aria-expanded='true'
onClick={(e) => this.changeTab(e, t)}
>
{t}
</a>
</li>
);
});
return (
<div className='hpanel'>
<ul className='nav nav-tabs'>
{tabs}
</ul>
<div className='tab-content'>
<div className='tab-pane active'>
<div className='panel-body'>
{this.props.tabs[this.state.tab]}
</div>
</div>
</div>
</div>
);
}
return <div/>;
}
}
Panel.propTypes = {
tabNames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
tabs: React.PropTypes.array.isRequired
};
{
"debug": true,
"zimbraUrl": "http://zimbra.zboxapp.dev:8000/service/admin/soap",
"zimbraProxy": "https://192.168.1.8:7071"
"zimbraProxy": "https://127.0.0.1:7071"
}
......@@ -14,6 +14,7 @@ import Accounts from './components/accounts/accounts.jsx';
import CreateAccount from './components/accounts/create_account.jsx';
import EditAccount from './components/accounts/edit_account.jsx';
import Domains from './components/domain/domains.jsx';
import DomainDetails from './components/domain/domain_details.jsx';
import CreateDomains from './components/domain/create_domain.jsx';
import EditDomains from './components/domain/edit_domain.jsx';
import MailBox from './components/mailbox/mailbox.jsx';
......@@ -107,17 +108,19 @@ function renderRootComponent() {
<Route
path='domains'
component={Domains}
>
<Route
path='new'
component={CreateDomains}
/>
<Route
path=':id/edit'
component={EditDomains}
/>
</Route>
/>
<Route
path='domains/new'
component={CreateDomains}
/>
<Route
path='domains/:id'
component={DomainDetails}
/>
<Route
path='domains/:id/edit'
component={EditDomains}
/>
<Route
path='accounts'
......
......@@ -1256,3 +1256,9 @@ fieldset {
width: 70px;
}
}
.panel-heading {
.btn {
margin-left: 5px;
}
}
......@@ -134,3 +134,15 @@
border-color: $border-color $border-color $border-color transparent;
z-index: 1;
}
.tab-content {
.panel-body {
.panel-heading {
border: 0;
}
.panel-body {
border: 0;
}
}
}
.domain-info {
.domain-name {
color: $color-green;
font-size: 22px;
}
.account-name {
color: $account-link-color;
font-size: 16px;
&:hover {
color: $color-blue;
}
}
}
.add-button::before {
content: '+';
font-weight: bold;
margin-right: 5px;
}
@import 'landing';
@import 'login';
@import 'domain';
......@@ -81,3 +81,6 @@ $flash-error-color: #a94442;
//Pretty Inputs
$border-color-input: #ccc;
$bg-border-color: #63a758;
$account-link-color: #337ab7;
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
class DomainStoreClass {
constructor() {
this.current = null;
}
getCurrent() {
return this.current;
}
setCurrent(domain) {
this.current = domain;
}
getCurrentId() {
const domain = this.getCurrent();
if (domain) {
return domain.id;
}
return null;
}
}
const DomainStore = new DomainStoreClass();
export {DomainStore as default};
......@@ -14,7 +14,9 @@ import Constants from './constants.jsx';
// función que maneja el error como corresponde
function handleError(methodName, err) {
if (err.extra && err.extra.code === Constants.ZimbraCodes.NOT_LOGGED_IN) {
if (err.extra &&
(err.extra.code === Constants.ZimbraCodes.AUTH__REQUIRED || err.extra.code === Constants.ZimbraCodes.AUTH__REQUIRED)
) {
Utils.setCookie('token', '', -1);
return err;
}
......@@ -154,3 +156,22 @@ export function getAllDomains(opts, success, error) {
}
);
}
export function getDomain(id, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.getDomain(id, (err, data) => {
if (err) {
let e = handleError('getAllDomain', err);
return error(e);
}
return success(data);
});
},
(err) => {
let e = handleError('getAllDomain', err);
return error(e);
}
);
}
......@@ -17,7 +17,8 @@ export default {
}),
ZimbraCodes: {
NOT_LOGGED_IN: 'service.AUTH_EXPIRED'
AUTH_EXPIRED: 'service.AUTH_EXPIRED',
AUTH__REQUIRED: 'service.AUTH_REQUIRED'
},
RESERVED_USERNAMES: [
......
......@@ -24,3 +24,7 @@ export function getCookie(cname) {
}
return '';
}
export function slug(str) {
return str.toLowerCase().replace(/ /g, '_');
}
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