Commit 03047062 authored by Elias Nahum's avatar Elias Nahum

Companies with details for each company

- Update dependencies
parent 07deed30
......@@ -11,6 +11,7 @@ const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const Promise = require('bluebird');
const _ = require('lodash');
const moment = require('moment');
const port = normalizePort(process.env.PORT || '8001'); //eslint-disable-line no-process-env
......@@ -53,7 +54,35 @@ app.get('/company/:id', (req, res) => {
});
app.get('/company/:id/invoices', (req, res) => {
return res.json([]);
return res.json([
{
number: 355,
link: 'http://google.com',
date: moment('2016-01-01').toJSON(),
total: '$ 54.490',
status: 1
},
{
number: 356,
link: 'http://google.com',
date: moment('2016-02-01').toJSON(),
total: '$ 54.490',
status: 2
},
{
number: 357,
date: moment('2016-02-01').toJSON(),
total: '$ 54.490',
status: 3
},
{
number: 358,
link: 'http://google.com',
date: moment('2016-03-01').toJSON(),
total: '$ 54.490',
status: 0
}
]);
});
// catch 404 and forward to error handler
......
......@@ -2,5 +2,17 @@
{
"id": "24346664-4",
"name": "ZBox"
},
{
"id": "13834853-9",
"name": "Patricio Bruna"
},
{
"id": "76530890-9",
"name": "IT Linux"
},
{
"id": "76424135-5",
"name": "Test Company"
}
]
......@@ -3,45 +3,48 @@
"version": "0.0.1",
"private": true,
"dependencies": {
"animate.css": "^3.5.1",
"bluebird": "^3.3.5",
"bootstrap": "3.3.6",
"compass-mixins": "0.12.7",
"flux": "2.1.1",
"font-awesome": "4.5.0",
"font-awesome": "4.6.1",
"jasny-bootstrap": "3.1.3",
"jquery": "2.2.3",
"keymirror": "0.1.1",
"lodash": "^4.11.1",
"moment": "^2.13.0",
"nprogress": "^0.2.0",
"object-assign": "4.0.1",
"perfect-scrollbar": "0.6.10",
"react": "15.0.0",
"react-bootstrap": "0.28.5",
"react": "15.0.1",
"react-bootstrap": "0.29.2",
"react-bootstrap-datetimepicker": "0.0.22",
"react-datalist": "^3.0.0",
"react-dom": "15.0.0",
"react-router": "2.0.1",
"react-textarea-autosize": "3.3.1",
"zimbra-admin-api-js": "ZBoxApp/zimbra-admin-api-js#umd"
"react-dom": "15.0.1",
"react-router": "2.3.0",
"react-textarea-autosize": "4.0.0",
"react-toastr": "^2.6.0",
"toastr": "^2.1.2",
"zimbra-admin-api-js": "ZBoxApp/zimbra-admin-api-js#master"
},
"devDependencies": {
"babel-cli": "^6.7.5",
"babel-eslint": "6.0.2",
"babel-eslint": "6.0.4",
"babel-loader": "6.2.4",
"babel-plugin-transform-runtime": "6.7.5",
"babel-polyfill": "6.7.4",
"babel-preset-es2015-webpack": "6.4.0",
"babel-preset-es2015-webpack": "6.4.1",
"babel-preset-react": "6.5.0",
"babel-preset-stage-0": "6.5.0",
"babel-runtime": "^6.6.1",
"body-parser": "^1.15.0",
"cookie-parser": "^1.4.1",
"copy-webpack-plugin": "1.1.1",
"copy-webpack-plugin": "2.1.3",
"cors": "^2.7.1",
"css-loader": "0.23.1",
"debug": "^2.2.0",
"eslint": "2.7.0",
"eslint-plugin-react": "4.3.0",
"eslint": "2.8.0",
"eslint-plugin-react": "5.0.1",
"exports-loader": "0.6.3",
"express": "^4.13.4",
"extract-text-webpack-plugin": "1.0.1",
......@@ -50,9 +53,8 @@
"http-proxy": "^1.13.2",
"imports-loader": "0.6.5",
"json-loader": "0.5.4",
"lodash": "^4.11.1",
"morgan": "^1.7.0",
"node-sass": "3.4.2",
"node-sass": "3.6.0",
"raw-loader": "0.5.1",
"sass-loader": "3.2.0",
"style-loader": "0.13.1",
......
......@@ -30,3 +30,11 @@ export function saveUser(user) {
user
});
}
export function showAlert(message) {
AppDispatcher.handleViewAction({
type: ActionTypes.NEW_TOAST,
message
});
}
// 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 PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
export default class Accounts extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleLink = this.handleLink.bind(this);
}
componentDidMount() {
$('#sidebar-accounts').addClass('active');
GlobalActions.emitEndLoading();
}
componentWillUnmount() {
$('#sidebar-accounts').removeClass('active');
}
handleLink(e, path) {
e.preventDefault();
browserHistory.push(path);
}
render() {
const addAccountButton = [{
label: 'Agregar Cuenta',
props: {
className: 'btn btn-success',
onClick: (e) => {
this.handleLink(e, '/accounts/new');
}
}
}];
const panelBody = (
<div className='center-block text-center'>
<h5>{'Sin resultados para su busqueda.'}</h5>
</div>
);
const pageInfo = (
<PageInfo
titlePage='Cuentas'
descriptionPage='Las cuentas son los que pagan el servicio'
/>
);
const hasPageInfo = (this.props.children) ? '' : pageInfo;
const indexView = (
<Panel
btnsHeader={addAccountButton}
children={panelBody}
/>
);
const views = this.props.children || indexView;
return (
<div>
{hasPageInfo}
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-12 central-content'>
{views}
</div>
</div>
</div>
</div>
);
}
}
Accounts.propTypes = {
children: React.PropTypes.any
};
import React from 'react';
import {browserHistory} from 'react-router';
import Panel from '../panel.jsx';
import Button from '../button.jsx';
export default class CreateAccount extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
notification: false,
notificationMsg: ''
};
}
handleClick(e, path) {
e.preventDefault();
browserHistory.push(path);
}
render() {
let form = (
<form className='simple_form form-horizontal mailbox-form'>
<div className='form-group string required'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'Nombre'}
</label>
<div className='col-sm-8'>
<input
type='text'
required='required'
className='form-control'
ref='companyName'
placeholder='Razón social de la empresa'
/>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'ID Cliente'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control select required'
required='required'
ref='idclient'
placeholder='Ingresa el RUT de la empresa (xxxxxxxx-y)'
>
</input>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Reseller'}
</label>
<div className='col-sm-8'>
<label className='radio-inline pretty-input'>
<div className='pretty-checkbox'>
<input
type='checkbox'
className='pretty'
ref='reseller'
/>
<span></span>
</div>
</label>
</div>
</div>
<div className='form-group'>
<div className='col-sm-8 col-sm-offset-3'>
<input
type='submit'
name='commit'
defaulValue='Guardar'
className='btn btn-info'
/>
<Button
btnAttrs={
{
className: 'btn btn-default',
onClick: (e) => {
this.handleClick(e, '/accounts');
}
}
}
>
{'Cancelar'}
</Button>
</div>
</div>
</form>
);
const actions = [
{
label: 'Cancelar',
props: {
className: 'btn btn-default btn-xs',
onClick: (e) => {
this.handleClick(e, '/accounts');
}
}
}
];
return (
<Panel
title={'Agregar Cuenta'}
btnsHeader={actions}
classHeader={'forum-box'}
>
{form}
</Panel>
);
}
}
import React from 'react';
import {browserHistory} from 'react-router';
import Panel from '../panel.jsx';
import Button from '../button.jsx';
export default class EditAccount extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
notification: false,
notificacionMsg: ''
};
}
handleClick(e, path) {
e.preventDefault();
browserHistory.push(path);
}
render() {
let form = (
<form className='simple_form form-horizontal mailbox-form'>
<div className='form-group string required'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'Nombre'}
</label>
<div className='col-sm-8'>
<input
type='text'
required='required'
className='form-control'
ref='companyName'
placeholder='Razón social de la empresa'
/>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'ID Cliente'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control select required'
required='required'
ref='idclient'
placeholder='Ingresa el RUT de la empresa (xxxxxxxx-y)'
>
</input>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Reseller'}
</label>
<div className='col-sm-8'>
<label className='radio-inline pretty-input'>
<div className='pretty-checkbox'>
<input
type='checkbox'
className='pretty'
ref='reseller'
/>
<span></span>
</div>
</label>
</div>
</div>
<div className='form-group'>
<div className='col-sm-8 col-sm-offset-3'>
<input
type='submit'
name='commit'
defaulValue='Guardar'
className='btn btn-info'
/>
<Button
btnAttrs={
{
className: 'btn btn-default',
onClick: (e) => {
this.handleClick(e, '/accounts');
}
}
}
>
{'Cancelar'}
</Button>
</div>
</div>
</form>
);
const actions = [
{
label: 'Cancelar',
props: {
className: 'btn btn-default btn-xs',
onClick: (e) => {
this.handleClick(e, '/accounts');
}
}
}
];
return (
<Panel
title={'Editar Cuenta'}
btnsHeader={actions}
classHeader={'forum-box'}
>
{form}
</Panel>
);
}
}
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import $ from 'jquery';
import React from 'react';
import Promise from 'bluebird';
import PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx';
import CompaniesStore from '../../stores/company_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
const messageTypes = Constants.MessageType;
export default class Companies extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getCompanies = this.getCompanies.bind(this);
this.getDomains = this.getDomains.bind(this);
this.getPlans = this.getPlans.bind(this);
this.getAdmins = this.getAdmins.bind(this);
this.gotoCompany = this.gotoCompany.bind(this);
}
gotoCompany(e, company) {
CompaniesStore.setCurrent(company);
Utils.handleLink(e, `/companies/${company.id}`, this.props.location);
}
getCompanies() {
const self = this;
const companies = CompaniesStore.getCompanies();
if (companies) {
self.setState({
companies
});
return GlobalActions.emitEndLoading();
}
return Client.getAllCompanies().then((data) => {
const domains = data.map((d) => {
return self.getDomains(d);
});
return Promise.all(domains).then((comps) => {
CompaniesStore.setCompanies(comps);
self.setState({
companies: comps
});
}).
catch((error) => {
self.setState({error: {
message: error,
type: messageTypes.ERROR
}});
});
}).catch((error) => {
self.setState({error});
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
getDomains(company) {
const self = this;
return new Promise((resolve, reject) => {
return Client.getAllDomains(
{
query: `businessCategory=${company.id}`
},
(data) => {
const domains = data.domain;
Promise.all([self.getPlans(domains), self.getAdmins(domains)]).
then(() => {
company.domains = domains;
resolve(company);
}).catch((error) => {
reject(error);
});
},
(error) => {
reject(error);
});
});
}
getAdmins(domains) {
return new Promise((resolve, reject) => {
const promises = domains.map((d) => {
return new Promise((solve, rej) => {
return d.getAdmins((err, admins) => {
if (err) {
return rej(err);
}
d.admins = admins;
return solve(d);
});
});
});
Promise.all(promises).
then((doms) => {
resolve(doms);
}).
catch((error) => {
reject(error);
});
});
}
getPlans(domains) {
const names = domains.map((d) => {
return d.name;
});
return new Promise((resolve, reject) => {
return Client.batchCountAccount(
names,
(data) => {
domains.forEach((d, i) => {
d.plans = data[i];
});
resolve(domains);
},
(error) => {
reject(error);
}
);
});
}
componentDidMount() {
$('#sidebar-companies').addClass('active');
this.getCompanies();
}
componentWillUnmount() {
$('#sidebar-companies').removeClass('active');
}
render() {
if (!this.state.companies) {
return <div/>;
}
let panelBody;
if (this.state.companies.length === 0) {
panelBody = (
<div className='center-block text-center'>
<h5>
{'Actualmente no hay ninguna empresa registrada '}
<label style={{transform: 'rotate(90deg)'}}>
{':=('}
</label>
</h5>
</div>
);
} else {
const rows = this.state.companies.map((c) => {
const plans = Utils.getPlansFromDomains(c.domains);
const plansString = [];
const totalBought = Object.keys(plans).reduce((prev, current) => {
const limit = plans[current].limit;
plansString.push(`${limit} ${Utils.titleCase(current.slice(0, 3))}`); //eslint-disable-line no-undefined
if (plans[prev]) {
return plans[prev].limit + limit;
}
return limit;
});
return (
<tr key={c.id}>
<td className='company-name'>
<a
href='#'
onClick={(e) => this.gotoCompany(e, c)}
>
{c.name}
</a>
<br/>
<small>
{c.id}
</small>
</td>
<td className='company-mbxs'>
<span className='total-mbxs'>{totalBought}</span>
<br/>
<small>{plansString.join(' | ')}</small>
</td>
</tr>
);
});
panelBody = (
<div className='table-responsive'>
<div className='table-responsive'>
<table
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align'
>
<thead>
<tr>
<th>{'Nombre'}</th>
<th className='text-center'>{'Casillas compradas'}</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
);
}
return (
<div>
<PageInfo
titlePage='Empresas'
descriptionPage='Las empresas son los que pagan el servicio'
/>
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
hasHeader={false}
children={panelBody}
/>
</div>
</div>
</div>
</div>
);
}
}
Companies.propTypes = {
location: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import _ from 'lodash';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class CompanyAdmins extends React.Component {
constructor(props) {
super(props);
this.getCompanyAdmins = this.getCompanyAdmins.bind(this);
this.state = this.getCompanyAdmins();
}
getCompanyAdmins() {
const domains = this.props.company.domains;
const admins = [];
if (domains) {
domains.forEach((d) => {
Reflect.apply(Array.prototype.push, admins, d.admins);
});
}
return {
admins: _.uniqBy(admins, 'id')
};
}
render() {
const admins = this.state.admins;
let panelBody;
if (admins.length > 0) {
const rows = admins.map((a) => {
let globalAdmin = a.attrs.zimbraIsAdminAccount === 'TRUE';
let adminClass = '';
if (globalAdmin) {
adminClass = 'btn btn-xs btn-danger';
globalAdmin = 'global admin';
} else {
adminClass = 'btn btn-xs btn-info';
globalAdmin = 'domain admin';
}
return (
<tr
key={`admin-${a.id}`}
className='user-row'
>
<td className='user-email'>
{a.name}
</td>
<td className='user-name text-center'>
{a.attrs.cn} {a.attrs.sn}
</td>
<td className='user-type text-center'>
<StatusLabel
classes={adminClass}
children={globalAdmin}
/>
</td>
<td className='user-actions text-center'>
<a
className='btn btn-default btn-xs'
href='#'
onClick={(e) => Utils.handleLink(e, `/mailboxes/${a.id}/edit`, this.props.location)}
>
{'Editar'}
</a>
</td>
</tr>
);
});
panelBody = (
<div className='table-responsive'>
<table
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align'
>
<thead>
<tr>
<th>{'email'}</th>
<th className='td-mbxs text-center'>{'Nombre'}</th>
<th className='text-center'>{'Perfil'}</th>
<th className='text-center'>{'Acciones'}</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
} else {
panelBody = (
<div className='empty-message text-danger'>
<h4>
{'Esta empresa no tiene administradores de dominio registrados.'}
</h4>
</div>
);
}
return (
<Panel
hasHeader={false}
children={panelBody}
/>
);
}
}
CompanyAdmins.propTypes = {
company: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import $ from 'jquery';
import React from 'react';
import Promise from 'bluebird';
import CompanyInfo from './company_info.jsx';
import CompanyMailboxPlans from './company_mailbox_plans.jsx';
import CompanyDomains from './company_domains.jsx';
import CompanyAdmins from './company_admins.jsx';
import CompanyInvoices from './company_invoices.jsx';
import MessageBar from '../message_bar.jsx';
import PageInfo from '../page_info.jsx';
import PanelTab from '../panel_tab.jsx';
import CompaniesStore from '../../stores/company_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
const messageTypes = Constants.MessageType;
export default class CompaniesDetails extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.getCompany = this.getCompany.bind(this);
this.getDomains = this.getDomains.bind(this);
this.getAdmins = this.getAdmins.bind(this);
this.getPlans = this.getPlans.bind(this);
}
getCompany() {
const self = this;
const companyId = this.props.params.id;
const company = CompaniesStore.getCurrent();
if (company) {
self.setState({
company
});
return GlobalActions.emitEndLoading();
}
return Client.getCompany(companyId).then((data) => {
return self.getDomains(data).then((comp) => {
CompaniesStore.setCurrent(comp);
self.setState({
company: comp
});
}).
catch((error) => {
self.setState({error: {
message: error.message,
type: messageTypes.ERROR
}});
});
}).catch((error) => {
self.setState({error});
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
getDomains(company) {
const self = this;
return new Promise((resolve, reject) => {
return Client.getAllDomains(
{
query: `businessCategory=${company.id}`
},
(data) => {
const domains = data.domain;
Promise.all([self.getPlans(domains), self.getAdmins(domains)]).
then(() => {
company.domains = domains;
resolve(company);
}).catch((error) => {
reject(error);
});
},
(error) => {
reject(error);
});
});
}
getAdmins(domains) {
return new Promise((resolve, reject) => {
const promises = domains.map((d) => {
return new Promise((solve, rej) => {
return d.getAdmins((err, admins) => {
if (err) {
return rej(err);
}
d.admins = admins;
return solve(d);
});
});
});
Promise.all(promises).
then((doms) => {
resolve(doms);
}).
catch((error) => {
reject(error);
});
});
}
getPlans(domains) {
const names = domains.map((d) => {
return d.name;
});
return new Promise((resolve, reject) => {
return Client.batchCountAccount(
names,
(data) => {
domains.forEach((d, i) => {
d.plans = data[i];
});
resolve(domains);
},
(error) => {
reject(error);
}
);
});
}
componentDidMount() {
$('#sidebar-companies').addClass('active');
this.getCompany();
}
componentWillUnmount() {
$('#sidebar-companies').removeClass('active');
}
render() {
const company = this.state.company;
if (!company) {
return <div/>;
}
let message;
if (this.state.error) {
message = (
<MessageBar
message={this.state.error.message}
type={this.state.error.type || messageTypes.ERROR}
autoclose={true}
/>
);
}
const tabAdmins = (
<CompanyAdmins
company={company}
location={this.props.location}
/>
);
const tabDomains = (
<CompanyDomains
company={company}
location={this.props.location}
/>
);
const panelTabs = (
<PanelTab
tabNames={['Administradores de dominio', 'Dominios']}
tabs={{
administradores_de_dominio: tabAdmins,
dominios: tabDomains
}}
location={this.props.location}
/>
);
return (
<div>
<PageInfo
titlePage={`Empresa: ${company.name}`}
descriptionPage='Las empresas son los que pagan el servicio'
/>
{message}
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-6 central-content'>
<CompanyInfo company={company}/>
</div>
<div className='col-md-6 central-content'>
<CompanyMailboxPlans company={company}/>
</div>
</div>
<div className='row'>
<div className='col-md-12 central-content'>
{panelTabs}
</div>
</div>
<div className='row'>
<div className='col-md-12 central-content'>
<CompanyInvoices
company={company}
location={this.props.location}
/>
</div>
</div>
</div>
</div>
);
}
}
CompaniesDetails.propTypes = {
location: React.PropTypes.object.isRequired,
params: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import UserStore from '../../stores/user_store.jsx';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class CompanyDomains extends React.Component {
render() {
const domains = this.props.company.domains;
const isAdmin = UserStore.isGlobalAdmin();
let buttons;
if (isAdmin) {
buttons = [{
label: 'Agregar Dominio',
props: {
className: 'btn btn-default btn-xs',
onClick: (e) => Utils.handleLink(e, '/domains/new', this.props.location)
}
}];
}
let panelBody;
if (domains.length > 0) {
const rows = domains.map((d) => {
let status = d.attrs.zimbraDomainStatus;
let statusClass = '';
switch (status) {
case 'locked':
statusClass = 'btn btn-xs btn-warning';
status = 'Bloqueado';
break;
case 'closed':
statusClass = 'btn btn-xs btn-danger';
status = 'Cerrado';
break;
default:
statusClass = 'btn btn-xs btn-info';
status = 'Activo';
break;
}
let totalAccounts = 0;
const plans = Utils.getPlansFromDomain(d);
const plansArray = Object.keys(plans).map((p) => {
const limit = plans[p].limit;
totalAccounts += limit;
return (
<li key={`domain-${d.id}-${p}`}>
{limit} {Utils.titleCase(p.slice(0, 3))}
</li>
);
});
return (
<tr
className='company-domain-row'
key={`domain-${d.id}`}
>
<td className='domain-name'>
<h4>
<a
href='#'
onClick={(e) => Utils.handleLink(e, `/domains/${d.id}`, this.props.location)}
>
{`@${d.name}`}
</a>
</h4>
</td>
<td className='company-domain-cell'>
<span className='total-mbxs-per-domain'>{totalAccounts}</span>
<ul className='list-inline'>
{plansArray}
</ul>
</td>
<td className='company-domain-cell'>
{d.attrs.description}
</td>
<td className='company-domain-cell'>
<StatusLabel
classes={statusClass}
children={status}
/>
</td>
</tr>
);
});
panelBody = (
<div className='table-responsive'>
<table
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align'
>
<thead>
<tr>
<th>{'Nombre'}</th>
<th className='td-mbxs text-center'>{'Casillas'}</th>
<th className='text-center'>{'Descripción'}</th>
<th className='text-center'>{'Estado'}</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
} else {
panelBody = (
<div className='empty-message text-danger'>
<h4>
{'Esta empresa no tiene dominios registrados.'}
</h4>
</div>
);
}
return (
<Panel
btnsHeader={buttons}
children={panelBody}
/>
);
}
}
CompanyDomains.propTypes = {
company: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Panel from '../panel.jsx';
export default class CompanyInfo extends React.Component {
render() {
const company = this.props.company;
const infoBody = (
<div className='account-info'>
<h4>
{company.name}
<br/>
<small>
{company.id}
</small>
</h4>
<p>
<br/><br/><br/><br/>
</p>
</div>
);
return (
<Panel
title='Información General'
children={infoBody}
/>
);
}
}
CompanyInfo.propTypes = {
company: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import moment from 'moment';
import _ from 'lodash';
import MessageBar from '../message_bar.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';
import Constants from '../../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class CompanyInvoices extends React.Component {
constructor(props) {
super(props);
this.getInvoices = this.getInvoices.bind(this);
this.state = {};
}
componentDidMount() {
this.getInvoices();
}
getInvoices() {
if (global.window.manager_config.companiesEndPoints.invoices) {
return Client.getInvoices(
this.props.company.id,
(invoices) => {
const hasDebt = _.find(invoices, {status: 2});
if (hasDebt) {
GlobalActions.showAlert({
type: 'error',
title: 'Cuenta con deuda',
body: 'Tiene una o más facturas impagas. Por favor corrija esta situación para prevenir un bloqueo de los servicios',
options: {
timeOut: 10000,
extendedTimeOut: 5000,
closeButton: true
}
});
}
this.setState({invoices});
},
(error) => {
this.setState({
error: {
message: error.message,
type: messageType.ERROR
}
});
}
);
}
return this.setState({
error: {
message: 'No se ha configurado la integración con el sistema de facturación.',
type: messageType.WARNING
}
});
}
render() {
const invoices = this.state.invoices;
const error = this.state.error;
let errorMessage;
if (error) {
errorMessage = (
<MessageBar
message={error.message}
type={error.type || messageType.ERROR}
canClose={false}
/>
);
}
let panelBody;
if (invoices) {
if (invoices.length > 0) {
const rows = invoices.map((i) => {
let status;
let statusClass = '';
let number = i.number;
if (i.link) {
number = (
<a
href={i.link}
target='_blank'
>
{i.number}
</a>
);
}
switch (i.status) {
case 0:
status = 'Pendiente';
statusClass = 'btn btn-xs btn-info';
break;
case 1:
status = 'Pagada';
statusClass = 'btn btn-xs btn-success';
break;
case 2:
status = 'Vencida';
statusClass = 'btn btn-xs btn-danger';
break;
default:
status = 'Anulada';
statusClass = 'btn btn-xs btn-default';
break;
}
return (
<tr key={`invoice-${i.number}`}>
<td>
{number}
</td>
<td className='text-center'>
{i.total}
</td>
<td className='text-center'>
{moment(i.date).locale('es').format('DD [de] MMMM [de] YYYY')}
</td>
<td className='text-center'>
<StatusLabel
classes={statusClass}
children={status}
/>
</td>
</tr>
);
});
panelBody = (
<div className='table-responsive'>
<table
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align'
>
<thead>
<tr>
<th>{'Número'}</th>
<th className='text-center'>{'Total'}</th>
<th className='text-center'>{'Fecha'}</th>
<th className='text-center'>{'Estado'}</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
} else {
errorMessage = (
<MessageBar
message='Esta empresa todavía no tiene facturas emitidas.'
type={messageType.INFO}
canClose={false}
/>
);
}
}
if (error || invoices) {
return (
<Panel
title='Facturas'
error={errorMessage}
children={panelBody}
/>
);
}
return <div/>;
}
}
CompanyInvoices.propTypes = {
company: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired
};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Panel from '../panel.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class CompanyMailboxPlans extends React.Component {
constructor(props) {
super(props);
this.handleBuy = this.handleBuy.bind(this);
}
handleBuy(e) {
e.preventDefault();
alert('llevar al usuario a la página que permita comprar más casillas'); //eslint-disable-line no-alert
}
render() {
const company = this.props.company;
const headerButtons = [
{
label: 'Comprar',
props: {
className: 'btn btn-info btn-xs',
onClick: this.handleBuy
}
}
];
const mailboxPlans = [];
const domains = company.domains;
const plans = Utils.getPlansFromDomains(domains);
for (const key in plans) {
if (plans.hasOwnProperty(key)) {
let freeClass = 'text-center';
let percent = 0;
let limit = 0;
let used = 0;
let free = 0;
const plan = plans[key];
limit = plan.limit;
used = plan.used;
if (limit) {
free = limit - used;
percent = Math.round((used * 100) / limit);
} else {
limit = free = '\u221e';
percent = 100;
}
if (percent <= 10) {
freeClass += ' alert-free-mbxs';
} else if (percent <= 20) {
freeClass += ' warning-free-mbxs';
}
mailboxPlans.push(
<tr key={`plan-${key}`}>
<td className='mbx-plan'
style={{borderTop: 0}}
>
{key}
</td>
<td
className='text-center'
style={{borderTop: 0}}
>
{limit}
</td>
<td
className='text-center'
style={{borderTop: 0}}
>
{used}
</td>
<td
className={freeClass}
style={{borderTop: 0}}
>
{free}
</td>
</tr>
);
}
}
const panelBody = (
<table
id='company-mbxs'
cellPadding='1'
cellSpacing='1'
className='table'
style={{marginBottom: '0px'}}
>
<thead>
<tr>
<th style={{width: '50%'}}></th>
<th className='text-center'>{'Límite'}</th>
<th className='text-center'>{'Usadas'}</th>
<th className='text-center'>{'Libres'}</th>
</tr>
</thead>
<tbody>
{mailboxPlans}
</tbody>
</table>
);
return (
<Panel
title='Casillas'
btnsHeader={headerButtons}
children={panelBody}
/>
);
}
}
CompanyMailboxPlans.propTypes = {
company: React.PropTypes.object.isRequired
};
......@@ -9,7 +9,6 @@ import Panel from '../panel.jsx';
import ToggleModalButton from '../toggle_modal_button.jsx';
import AddAdminModal from './add_admin_modal.jsx';
// import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class DomainAdminList extends React.Component {
......
......@@ -7,15 +7,20 @@ import moment from 'moment';
import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx';
import {getDnsInfo} from '../../utils/client.jsx';
import CompanyStore from '../../stores/company_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import Constant from '../../utils/constants.jsx';
export default class DomainGeneralInfo extends React.Component {
constructor(props) {
super(props);
this.getMXRecord = this.getMXRecord.bind(this);
this.renovationDate = this.renovationDate.bind(this);
this.getCompany = this.getCompany.bind(this);
this.state = {
mx: null,
......@@ -23,23 +28,50 @@ export default class DomainGeneralInfo extends React.Component {
};
}
componentWillMount() {
this.getMXRecord();
const domain = this.props.domain;
this.getMXRecord(domain.name);
this.getCompany(domain.attrs.businessCategory);
}
getMXRecord() {
getDnsInfo(
this.props.domain.name,
getMXRecord(name) {
const self = this;
Client.getDnsInfo(
name,
(data) => {
this.setState({
self.setState({
mx: data.mx
});
},
(err) => {
this.setState({
self.setState({
mx: err
});
}
);
}
getCompany(id) {
const company = CompanyStore.getCompanyById(id);
if (company) {
this.setState({
company: company.name
});
} else {
Client.getCompany(id).
then((data) => {
this.setState({
company: data.name
});
}).
catch((error) => {
this.setState({
error: {
message: error.message,
type: Constant.MessageType.ERROR
}
});
});
}
}
renovationDate() {
const timestamp = moment(this.props.domain.attrs.zimbraCreateTimestamp);
const now = moment();
......@@ -69,7 +101,7 @@ export default class DomainGeneralInfo extends React.Component {
className='account-name'
onClick={(e) => Utils.handleLink(e, `/accounts/${domain.id_empresa}`, this.props.location)}
>
{'Nombre de la Empresa'}
{this.state.company}
</a>
</p>
<ul className='list-unstyled'>
......
......@@ -127,8 +127,8 @@ export default class DomainMailboxPlans extends React.Component {
<thead>
<tr>
<th style={{width: '50%'}}></th>
<th className='text-center'>Límite</th>
<th className='text-center'>Usadas</th>
<th className='text-center'>{'Límite'}</th>
<th className='text-center'>{'Usadas'}</th>
</tr>
</thead>
<tbody>
......
......@@ -18,6 +18,7 @@ import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
const QueryOptions = Constants.QueryOptions;
const messageType = Constants.MessageType;
export default class Domains extends React.Component {
constructor(props) {
......@@ -36,7 +37,7 @@ export default class Domains extends React.Component {
const self = this;
Client.getAllDomains(
{
limit: QueryOptions.DEFAULT_LIMIT,
limit: 200,
offset: this.state.offset
},
(data) => {
......@@ -109,7 +110,7 @@ export default class Domains extends React.Component {
message = (
<MessageBar
message={this.state.error}
type='success'
type={messageType.WARNING}
autoclose={true}
/>
);
......
......@@ -4,6 +4,7 @@
import LoadingScreen from './loading_screen.jsx';
import Header from './header.jsx';
import Sidebar from './sidebar.jsx';
import ToastAlert from './toast_alert.jsx';
import React from 'react';
......@@ -11,6 +12,7 @@ export default class LoggedIn extends React.Component {
render() {
return (
<div>
<ToastAlert/>
<LoadingScreen/>
<Header/>
<Sidebar location={this.props.location}/>
......
......@@ -13,6 +13,8 @@ import Panel from '../panel.jsx';
import LoginEmail from './login_email.jsx';
import MessageBar from '../message_bar.jsx';
const messageTypes = Constants.MessageType;
export default class Login extends React.Component {
constructor(props) {
super(props);
......@@ -30,7 +32,7 @@ export default class Login extends React.Component {
componentDidMount() {
Client.isLoggedIn((data) => {
if (data && data.logged_in) {
browserHistory.push('/accounts');
browserHistory.push('/companies');
} else {
$('body').addClass('blank');
}
......@@ -42,7 +44,7 @@ export default class Login extends React.Component {
this.setState({user});
if (user) {
browserHistory.push('/accounts');
browserHistory.push('/companies');
}
}
submit(email, password) {
......@@ -64,7 +66,7 @@ export default class Login extends React.Component {
Client.login(email, password,
() => {
browserHistory.push('/accounts');
browserHistory.push('/companies');
},
(err) => {
this.setState({loginError: err.message});
......@@ -81,7 +83,7 @@ export default class Login extends React.Component {
loginError = (
<MessageBar
message={this.state.loginError}
type='error'
type={messageTypes.ERROR}
position='relative'
canClose={false}
/>
......@@ -90,7 +92,7 @@ export default class Login extends React.Component {
loginError = (
<MessageBar
message='Tu sesión a expirado. Por favor ingresa nuevamente.'
type='info'
type={messageTypes.INFO}
position='relative'
canClose={false}
/>
......@@ -99,7 +101,7 @@ export default class Login extends React.Component {
loginError = (
<MessageBar
message='Necesitas iniciar sesión o registrarte para continuar.'
type='error'
type={messageTypes.ERROR}
position='relative'
canClose={false}
/>
......
......@@ -8,6 +8,10 @@ import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import Constants from '../../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class CreateMailBox extends React.Component {
constructor(props) {
super(props);
......@@ -37,7 +41,7 @@ export default class CreateMailBox extends React.Component {
this.setState(
{
error: `Su cuenta ${data.name} ha sido creada con èxito.`,
typeError: 'success'
typeError: messageType.SUCCESS
}
);
},
......@@ -45,7 +49,7 @@ export default class CreateMailBox extends React.Component {
this.setState(
{
error: error.message,
typeError: 'warning'
typeError: messageType.WARNING
}
);
}
......
......@@ -11,6 +11,10 @@ import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Utils from '../../utils/utils.jsx';
import Constants from '../../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class EditMailBox extends React.Component {
constructor(props) {
super(props);
......@@ -45,7 +49,7 @@ export default class EditMailBox extends React.Component {
this.setState(
{
error: `Su cuenta ${data.name} ha sido modificada con èxito.`,
typeError: 'success',
typeError: messageType.SUCCESS,
data: data
}
);
......@@ -54,7 +58,7 @@ export default class EditMailBox extends React.Component {
this.setState(
{
error: error.message,
typeError: 'warning'
typeError: messageType.WARNING
}
);
Utils.toggleStatusButtons('.action-button', false);
......
......@@ -21,6 +21,7 @@ import Constants from '../../utils/constants.jsx';
import DomainStore from '../../stores/domain_store.jsx';
const QueryOptions = Constants.QueryOptions;
const messageType = Constants.MessageType;
export default class Mailboxes extends React.Component {
constructor(props) {
......@@ -159,7 +160,7 @@ export default class Mailboxes extends React.Component {
message = (
<MessageBar
message={this.state.error}
type='success'
type={messageType.SUCCESS}
autoclose={true}
/>
);
......
......@@ -4,6 +4,10 @@
import $ from 'jquery';
import React from 'react';
import Constants from '../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class MessageBar extends React.Component {
constructor(props) {
super(props);
......@@ -60,18 +64,20 @@ export default class MessageBar extends React.Component {
);
}
const alertClass = `alert flash-${this.props.type} ${dismissible}`;
const alertClass = `alert flash-${this.props.type.toLowerCase()} ${dismissible}`;
let icon;
switch (this.props.type) {
case 'error':
case messageType.ERROR:
icon = (<i className='fa fa-exclamation-circle'></i>);
break;
case 'info':
case messageType.INFO:
icon = (<i className='fa fa-info-circle'></i>);
break;
case 'success':
case messageType.SUCCESS:
icon = (<i className='fa fa-check-circle'></i>);
break;
case 'warning':
case messageType.WARNING:
icon = (<i className='fa fa-exclamation-triangle'></i>);
break;
}
......@@ -106,7 +112,7 @@ MessageBar.defaultProps = {
MessageBar.propTypes = {
message: React.PropTypes.string.isRequired,
type: React.PropTypes.oneOf(['success', 'error', 'warning', 'info']),
type: React.PropTypes.oneOf(['SUCCESS', 'ERROR', 'WARNING', 'INFO']),
position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit']),
canClose: React.PropTypes.bool,
autoclose: React.PropTypes.bool,
......
......@@ -21,7 +21,7 @@ export default class Root extends React.Component {
if (!data || !data.logged_in) {
browserHistory.push('/login');
} else {
browserHistory.push('/accounts');
browserHistory.push('/companies');
}
});
}
......
......@@ -32,12 +32,12 @@ export default class SidebarMenu extends React.Component {
<span className='nav-label'>{'dashboards'}</span>
</a>
</li>
<li id='sidebar-accounts'>
<li id='sidebar-companies'>
<a
href='#'
onClick={(e) => this.handleLink(e, '/accounts')}
onClick={(e) => this.handleLink(e, '/companies')}
>
<span className='nav-label'>{'cuentas'}</span>
<span className='nav-label'>{'Empresas'}</span>
</a>
</li>
<li id='sidebar-domains'>
......
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {ToastContainer, ToastMessage} from 'react-toastr';
import EventStore from '../stores/event_store.jsx';
const ToastMessageFactory = React.createFactory(ToastMessage.animation);
export default class ToastAlert extends React.Component {
constructor(props) {
super(props);
this.addAlert = this.addAlert.bind(this);
}
componentDidMount() {
EventStore.addToastListener(this.addAlert);
}
componentWillUnmount() {
EventStore.removeToastListener(this.addAlert);
}
addAlert(message) {
this.refs.alertContainer[message.type](
message.body,
message.title,
message.options
);
}
render() {
return (
<div>
<ToastContainer
ref='alertContainer'
toastMessageFactory={ToastMessageFactory}
className='toast-top-center'
/>
</div>
);
}
}
{
"debug": false,
"debug": true,
"zimbraUrl": "http://zimbra.zboxapp.dev:8000/service/admin/soap",
"zimbraProxy": "https://zimbra.zboxapp.dev:7071",
"dnsApiUrl": "http://zimbra.zboxapp.dev:3000",
......@@ -8,5 +8,10 @@
"premium": true,
"professional": true,
"default": false
},
"companiesEndPoints": {
"list": "http://zimbra.zboxapp.dev:8001/list",
"detail": "http://zimbra.zboxapp.dev:8001/company/{id}",
"invoices": "http://zimbra.zboxapp.dev:8001/company/{id}/invoices"
}
}
......@@ -10,9 +10,8 @@ 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 CreateAccount from './components/accounts/create_account.jsx';
import EditAccount from './components/accounts/edit_account.jsx';
import Companies from './components/companies/companies.jsx';
import Company from './components/companies/company_details.jsx';
import Domains from './components/domain/domains.jsx';
import DomainDetails from './components/domain/domain_details.jsx';
import CreateDomains from './components/domain/create_domain.jsx';
......@@ -139,19 +138,13 @@ function renderRootComponent() {
/>
<Route
path='accounts'
component={Accounts}
>
<Route
path='new'
component={CreateAccount}
/>
<Route
path=':id/edit'
component={EditAccount}
/>
</Route>
path='companies'
component={Companies}
/>
<Route
path='companies/:id'
component={Company}
/>
<Route
path='mailboxes'
......
.company-name {
a {
color: $color-green;
font-size: 16px;
font-weight: bold;
}
}
.company-mbxs {
text-align: center;
.total-mbxs {
font-size: 16px;
font-weight: bold;
}
}
table {
.alert-free-mbxs {
background: $color-red-deep;
color: $white;
}
.warning-free-mbxs {
background: $color-orange;
color: $white;
}
.company-domain-row {
.company-domain-cell {
padding: 0 20px;
text-align: center;
vertical-align: middle;
}
}
}
@import 'landing';
@import 'login';
@import 'domain';
@import 'companies';
......@@ -8,6 +8,8 @@
@import '~perfect-scrollbar/dist/css/perfect-scrollbar.css';
@import '~font-awesome/css/font-awesome.css';
@import '~nprogress/nprogress.css';
@import '~toastr/build/toastr.css';
@import '~animate.css';
// styles.scss
@import 'utils/module';
@import 'layout/module';
......
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
class CompanyStoreClass {
constructor() {
this.current = null;
this.companies = null;
}
getCompanies() {
const array = [];
const companies = this.companies;
if (!companies) {
return null;
}
for (const id in companies) {
if (companies.hasOwnProperty(id)) {
array.push(companies[id]);
}
}
return array;
}
getCompanyById(id) {
if (!this.companies) {
return null;
}
return this.companies[id];
}
getCurrent() {
return this.current;
}
setCompanies(companiesArray) {
const companies = {};
companiesArray.forEach((c) => {
companies[c.id] = c;
});
this.companies = companies;
}
setCurrent(company) {
this.current = company;
}
}
const CompanyStore = new CompanyStoreClass();
export {CompanyStore as default};
......@@ -44,6 +44,18 @@ class EventStoreClass extends EventEmitter {
removeMessageListener(callback) {
this.removeListener(eventTypes.NEW_MESSAGE_EVENT, callback);
}
emitToast(message) {
this.emit(eventTypes.NEW_TOAST_EVENT, message);
}
addToastListener(callback) {
this.on(eventTypes.NEW_TOAST_EVENT, callback);
}
removeToastListener(callback) {
this.removeListener(eventTypes.NEW_TOAST_EVENT, callback);
}
}
var EventStore = new EventStoreClass();
......@@ -62,6 +74,9 @@ EventStore.dispatchToken = AppDispatcher.register((payload) => {
case ActionTypes.NEW_MESSAGE:
EventStore.emitMessage(action.attrs);
break;
case ActionTypes.NEW_TOAST:
EventStore.emitToast(action.message);
break;
default:
}
});
......
......@@ -23,7 +23,7 @@ class UserStoreClass extends EventEmitter {
}
getCurrentId() {
var user = this.getCurrentUser();
const user = this.getCurrentUser();
if (user) {
return user.id;
......@@ -32,6 +32,16 @@ class UserStoreClass extends EventEmitter {
return null;
}
isGlobalAdmin() {
const user = this.getCurrentUser();
if (user) {
return user.attrs._attrs.zimbraIsAdminAccount === 'TRUE'; //eslint-disable-line no-underscore-dangle
}
return false;
}
emitChange() {
this.emit(eventTypes.USER_CHANGE_EVENT);
}
......
......@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import $ from 'jquery';
import Promise from 'bluebird';
import ZimbraAdminApi from 'zimbra-admin-api-js';
import ZimbraStore from '../stores/zimbra_store.jsx';
......@@ -23,7 +24,10 @@ function handleError(methodName, err) {
console.error(methodName, err); //eslint-disable-line no-console
const error = {};
const error = {
type: Constants.MessageType.ERROR
};
if (err) {
error.message = err.extra.reason;
} else {
......@@ -141,6 +145,53 @@ export function isLoggedIn(callback) {
return callback(data);
}
export function getAllCompanies() {
const url = global.window.manager_config.companiesEndPoints.list;
return new Promise((resolve, reject) => {
return $.ajax({
url,
dataType: 'json',
success: function onSuccess(data) {
resolve(data);
},
error: function onError(xhr, status, err) {
reject(err);
}
});
});
}
export function getCompany(id) {
const url = global.window.manager_config.companiesEndPoints.detail.replace('{id}', id);
return new Promise((resolve, reject) => {
return $.ajax({
url,
dataType: 'json',
success: function onSuccess(data) {
resolve(data);
},
error: function onError(xhr, status, err) {
reject(err);
}
});
});
}
export function getInvoices(id, success, error) {
const url = global.window.manager_config.companiesEndPoints.invoices.replace('{id}', id);
return $.ajax({
url,
dataType: 'json',
success,
error: function onError(xhr, status, err) {
error(err);
}
});
}
export function getAllDomains(opts, success, error) {
initZimbra().then(
(zimbra) => {
......@@ -331,6 +382,25 @@ export function countAccounts(domain, success, error) {
);
}
export function batchCountAccount(domains, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.batchCountAccounts(domains, (err, data) => {
if (err) {
const e = handleError('batchCountAccount', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('batchCountAccount', err);
return error(e);
}
);
}
export function getDnsInfo(domain, success, error) {
$.ajax({
url: `${global.window.manager_config.dnsApiUrl}/dns`,
......@@ -381,3 +451,26 @@ export function addAccountAlias(alias, success, error) {
}
);
}
export function search(query, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.directorySearch(
{
query
},
(err, data) => {
if (err) {
const e = handleError('search', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('search', err);
return error(e);
}
);
}
......@@ -9,7 +9,8 @@ export default {
END_LOADING: null,
USER_CHANGED: null,
RECEIVED_ERROR: null,
NEW_MESSAGE: null
NEW_MESSAGE: null,
NEW_TOAST: null
}),
PayloadSources: keyMirror({
......@@ -23,13 +24,15 @@ export default {
START_LOADING_EVENT: null,
END_LOADING_EVENT: null,
USER_CHANGE_EVENT: null,
NEW_MESSAGE_EVENT: null
NEW_MESSAGE_EVENT: null,
NEW_TOAST_EVENT: null
}),
MessageType: keyMirror({
SUCCESS: null,
WARNING: null,
ERROR: null
ERROR: null,
INFO: null
}),
ZimbraCodes: {
......
......@@ -227,3 +227,57 @@ export function removeIndexFromArray(array, index, pos) {
return array;
}
export function getPlansFromDomains(domains) {
const configPlans = global.window.manager_config.plans;
const plans = {};
for (const key in configPlans) {
if (configPlans.hasOwnProperty(key) && configPlans[key]) {
plans[key] = {
used: 0,
limit: 0
};
}
}
domains.forEach((d) => {
const pls = d.plans;
for (const key in pls) {
if (pls.hasOwnProperty(key) && plans.hasOwnProperty(key)) {
plans[key].used += pls[key].used;
plans[key].limit += (pls[key].limit || 0);
}
}
});
return plans;
}
export function getPlansFromDomain(domain) {
const configPlans = global.window.manager_config.plans;
const plans = {};
for (const key in configPlans) {
if (configPlans.hasOwnProperty(key) && configPlans[key]) {
plans[key] = {
used: 0,
limit: 0
};
}
}
const pls = domain.plans;
for (const key in pls) {
if (pls.hasOwnProperty(key) && plans.hasOwnProperty(key)) {
plans[key].used += pls[key].used;
plans[key].limit += (pls[key].limit || 0);
}
}
return plans;
}
export function titleCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
export default class Domain {
constructor(connection) {
this.zimbra = connection;
}
get(name, by, attrs, success, error) {
if (this.zimbra) {
this.zimbra.getRequest({}, (err, req) => {
if (err) {
return error(err);
}
return req.addRequest({
name: 'GetDomainRequest',
namespace: 'zimbraAdmin',
params: {
domain: {
attrs,
by,
_content: name
}
}
}, (er) => {
if (er) {
return error(er);
}
return this.zimbra.send(req, (err2, response) => {
if (err2) {
return error(err2);
}
return success(response.get().GetAccountInfoResponse);
});
});
});
}
}
}
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