Commit a55b10bb authored by Juorder Antonio's avatar Juorder Antonio

Merge branch 'master' of https://github.com/ZBoxApp/manager-react

parents 48a7327b 62029691
# ZBox Manager in REACT
## Configuration
The is a configuration file under `config/config.json` with the following structure
```json
{
"debug": true,
"zimbraUrl": "http://zimbra.zboxapp.dev:8000/service/admin/soap",
"zimbraProxy": "https://zimbra.zboxapp.dev:7071",
"dnsApiUrl": "http://zimbra.zboxapp.dev:3000",
"plans": {
"basic": true,
"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"
}
}
```
* **debug**: Use in development environmet to recieve messages in the javascript console.
* **zimbraUrl**: The URL where the zimbra admin services are running.
* **zimbraProxy**: The URL of a proxy server to allow CORS between the webapp and the Zimbra admin service.
* **dnsApiUrl**: URL of the DNS api to get DNS information ( [zDNS](https://github.com/ZBoxApp/zDNS) ).
* **plans**: Object with the available mailboxes plans, set to *true* if enabled or *false* to disable it.
* **companiesEndPoints**: This are the enpoints to get information about companies and invoices. (Checkout the companies endpoint specifications below).
* **list**: The Endpoint to get a list of available companies
* **detail**: The Endpoint to get information from a specific company. The *{id}* string paramenter is mandatory.
* **invoices**: The Endpoint to get information for the invoices of a specific company. The *{id}* string paramenter is mandatory.
## Companies endpoint specifications
The method of every endpoint should be `GET` (so far no security is being allowed).
### List
REST Service that returns a list of available companies.
* Content-Type: `application/json`
* Method: `GET`
Response should be a `json array` with the following structure:
```json
[
{
"id": "company-identifier",
"name": "company name"
}
]
```
### Detail
REST Service that returns the company specified by the {id} parameter.
* Content-Type: `application/json`
* Method: `GET`
Response should be a `json object` with the following structure:
```json
{
"id": "company-identifier",
"name": "company name"
}
```
### Invoices
REST Services that returns the invoices for the company specified by the {id} parameter.
* Content-Type: `application/json`
* Method: `GET`
Response should be a `json array` with the following structure:
```json
[
{
"number": 123,
"link": "optional url to view the invoice",
"date": "2016-04-30T19:33:00Z",
"total": "$ 12.000",
"status": 0
}
]
```
Posible status are:
| Status | Meaning |
| -------- | --------- |
| 0 | Pendiente |
| 1 | Pagada |
| 2 | Vencida |
| 3 | Anulada |
## TODO
* Add security to the companies Endpoints
...@@ -14,5 +14,9 @@ ...@@ -14,5 +14,9 @@
{ {
"id": "76424135-5", "id": "76424135-5",
"name": "Test Company" "name": "Test Company"
},
{
"id": "1233667482",
"name": "Parnasa"
} }
] ]
...@@ -5,10 +5,12 @@ import $ from 'jquery'; ...@@ -5,10 +5,12 @@ import $ from 'jquery';
import React from 'react'; import React from 'react';
import Promise from 'bluebird'; import Promise from 'bluebird';
import MessageBar from '../message_bar.jsx';
import PageInfo from '../page_info.jsx'; import PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import CompaniesStore from '../../stores/company_store.jsx'; import CompaniesStore from '../../stores/company_store.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import * as Client from '../../utils/client.jsx'; import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx'; import * as Utils from '../../utils/utils.jsx';
...@@ -27,7 +29,6 @@ export default class Companies extends React.Component { ...@@ -27,7 +29,6 @@ export default class Companies extends React.Component {
this.getCompanies = this.getCompanies.bind(this); this.getCompanies = this.getCompanies.bind(this);
this.getDomains = this.getDomains.bind(this); this.getDomains = this.getDomains.bind(this);
this.getPlans = this.getPlans.bind(this); this.getPlans = this.getPlans.bind(this);
this.getAdmins = this.getAdmins.bind(this);
this.gotoCompany = this.gotoCompany.bind(this); this.gotoCompany = this.gotoCompany.bind(this);
} }
...@@ -47,8 +48,8 @@ export default class Companies extends React.Component { ...@@ -47,8 +48,8 @@ export default class Companies extends React.Component {
} }
return Client.getAllCompanies().then((data) => { return Client.getAllCompanies().then((data) => {
const domains = data.map((d) => { const domains = data.map((company) => {
return self.getDomains(d); return self.getDomains(company);
}); });
return Promise.all(domains).then((comps) => { return Promise.all(domains).then((comps) => {
...@@ -80,13 +81,17 @@ export default class Companies extends React.Component { ...@@ -80,13 +81,17 @@ export default class Companies extends React.Component {
}, },
(data) => { (data) => {
const domains = data.domain; const domains = data.domain;
Promise.all([self.getPlans(domains), self.getAdmins(domains)]). if (domains) {
then(() => { self.getPlans(domains).then(() => {
company.domains = domains; company.domains = domains;
resolve(company);
}).catch((error) => {
reject(error);
});
} else {
company.domains = [];
resolve(company); resolve(company);
}).catch((error) => { }
reject(error);
});
}, },
(error) => { (error) => {
reject(error); reject(error);
...@@ -94,31 +99,6 @@ export default class Companies extends React.Component { ...@@ -94,31 +99,6 @@ export default class Companies extends React.Component {
}); });
} }
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.account;
return solve(d);
});
});
});
Promise.all(promises).
then((doms) => {
resolve(doms);
}).
catch((error) => {
reject(error);
});
});
}
getPlans(domains) { getPlans(domains) {
const names = domains.map((d) => { const names = domains.map((d) => {
return d.name; return d.name;
...@@ -155,6 +135,7 @@ export default class Companies extends React.Component { ...@@ -155,6 +135,7 @@ export default class Companies extends React.Component {
} }
let panelBody; let panelBody;
let noLimitError;
if (this.state.companies.length === 0) { if (this.state.companies.length === 0) {
panelBody = ( panelBody = (
<div className='center-block text-center'> <div className='center-block text-center'>
...@@ -168,17 +149,43 @@ export default class Companies extends React.Component { ...@@ -168,17 +149,43 @@ export default class Companies extends React.Component {
); );
} else { } else {
const rows = this.state.companies.map((c) => { const rows = this.state.companies.map((c) => {
const plans = Utils.getPlansFromDomains(c.domains); const cos = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
const plansString = []; const plansString = [];
const totalBought = Object.keys(plans).reduce((prev, current) => { const domains = c.domains;
const limit = plans[current].limit; const planKeys = Object.keys(cos).map((cosKey) => {
return cos[cosKey];
});
const plans = {};
plansString.push(`${limit} ${Utils.titleCase(current.slice(0, 3))}`); //eslint-disable-line no-undefined planKeys.forEach((key) => {
plans[key] = 0;
});
if (plans[prev]) { domains.forEach((d) => {
return plans[prev].limit + limit; const domainCos = d.maxAccountsByCos();
if (domainCos) {
Object.keys(domainCos).forEach((id) => {
const limit = domainCos[id];
plans[cos[id]] += limit;
});
} else if (!noLimitError) {
noLimitError = (
<MessageBar
message='Existen dominios sin límites asignados'
type='WARNING'
autoclose={true}
/>
);
} }
return limit; });
let totalBought = 0;
planKeys.forEach((key) => {
const limit = plans[key];
plansString.push(`${limit} ${Utils.titleCase(key.slice(0, 3))}`); //eslint-disable-line no-undefined
totalBought += limit;
}); });
return ( return (
<tr key={c.id}> <tr key={c.id}>
...@@ -203,6 +210,7 @@ export default class Companies extends React.Component { ...@@ -203,6 +210,7 @@ export default class Companies extends React.Component {
</tr> </tr>
); );
}); });
panelBody = ( panelBody = (
<div className='table-responsive'> <div className='table-responsive'>
<div className='table-responsive'> <div className='table-responsive'>
...@@ -232,6 +240,7 @@ export default class Companies extends React.Component { ...@@ -232,6 +240,7 @@ export default class Companies extends React.Component {
titlePage='Empresas' titlePage='Empresas'
descriptionPage='Las empresas son los que pagan el servicio' descriptionPage='Las empresas son los que pagan el servicio'
/> />
{noLimitError}
<div className='content animate-panel'> <div className='content animate-panel'>
<div className='row'> <div className='row'>
<div className='col-md-12 central-content'> <div className='col-md-12 central-content'>
......
...@@ -3,10 +3,13 @@ ...@@ -3,10 +3,13 @@
import React from 'react'; import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import Promise from 'bluebird';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx'; import StatusLabel from '../status_label.jsx';
import CompanyStore from '../../stores/company_store.jsx';
import * as Utils from '../../utils/utils.jsx'; import * as Utils from '../../utils/utils.jsx';
export default class CompanyAdmins extends React.Component { export default class CompanyAdmins extends React.Component {
...@@ -14,107 +17,173 @@ export default class CompanyAdmins extends React.Component { ...@@ -14,107 +17,173 @@ export default class CompanyAdmins extends React.Component {
super(props); super(props);
this.getCompanyAdmins = this.getCompanyAdmins.bind(this); this.getCompanyAdmins = this.getCompanyAdmins.bind(this);
this.getAdmins = this.getAdmins.bind(this);
this.state = this.getCompanyAdmins(); this.state = {};
} }
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.account;
return solve(d);
});
});
});
return Promise.all(promises).
then((doms) => {
return resolve(doms);
}).
catch((error) => {
return reject(error);
});
});
}
getCompanyAdmins() { getCompanyAdmins() {
const domains = this.props.company.domains; const company = this.props.company;
const domains = company.domains;
const admins = []; const admins = [];
const domainsArray = [];
if (domains) { if (domains) {
domains.forEach((d) => { domains.forEach((d) => {
if (d.admins) { if (d.admins) {
Reflect.apply(Array.prototype.push, admins, d.admins); Reflect.apply(Array.prototype.push, admins, d.admins);
} else {
domainsArray.push(d);
} }
}); });
} }
return { if (domainsArray.length > 0) {
return this.getAdmins(domainsArray).
then((doms) => {
doms.forEach((d) => {
if (!d.admins) {
d.admins = [];
}
CompanyStore.addDomainAdmins(company.id, d);
Reflect.apply(Array.prototype.push, admins, d.admins);
});
return this.setState({
admins: _.uniqBy(admins, 'id')
});
}).
catch(() => {
return this.setState({
error: {
message: 'No pudimos obtener los admins de los dominios',
type: 'ERROR'
}
});
});
}
return this.setState({
admins: _.uniqBy(admins, 'id') admins: _.uniqBy(admins, 'id')
}; });
}
componentDidMount() {
this.getCompanyAdmins();
} }
render() { render() {
const admins = this.state.admins; const admins = this.state.admins;
const error = this.state.error;
let panelBody; if (admins || error) {
if (admins.length > 0) { let panelBody;
const rows = admins.map((a) => { if (!error && admins.length > 0) {
let globalAdmin = a.attrs.zimbraIsAdminAccount === 'TRUE'; const rows = admins.map((a) => {
let adminClass = ''; let globalAdmin = a.attrs.zimbraIsAdminAccount === 'TRUE';
if (globalAdmin) { let adminClass = '';
adminClass = 'btn btn-xs btn-danger'; if (globalAdmin) {
globalAdmin = 'global admin'; adminClass = 'btn btn-xs btn-danger';
} else { globalAdmin = 'global admin';
adminClass = 'btn btn-xs btn-info'; } else {
globalAdmin = 'domain admin'; adminClass = 'btn btn-xs btn-info';
} globalAdmin = 'domain admin';
}
return ( return (
<tr <tr
key={`admin-${a.id}`} key={`admin-${a.id}`}
className='user-row' className='user-row'
> >
<td className='user-email'> <td className='user-email'>
{a.name} {a.name}
</td> </td>
<td className='user-name text-center'> <td className='user-name text-center'>
{a.attrs.cn} {a.attrs.sn} {a.attrs.cn} {a.attrs.sn}
</td> </td>
<td className='user-type text-center'> <td className='user-type text-center'>
<StatusLabel <StatusLabel
classes={adminClass} classes={adminClass}
children={globalAdmin} children={globalAdmin}
/> />
</td> </td>
<td className='user-actions text-center'> <td className='user-actions text-center'>
<a <a
className='btn btn-default btn-xs' className='btn btn-default btn-xs'
href='#' href='#'
onClick={(e) => Utils.handleLink(e, `/mailboxes/${a.id}/edit`, this.props.location)} onClick={(e) => Utils.handleLink(e, `/mailboxes/${a.id}/edit`, this.props.location)}
> >
{'Editar'} {'Editar'}
</a> </a>
</td> </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> </tr>
</thead> );
<tbody> });
{rows} panelBody = (
</tbody> <div className='table-responsive'>
</table> <table
</div> cellPadding='1'
); cellSpacing='1'
} else { className='table table-condensed table-striped vertical-align'
panelBody = ( >
<div className='empty-message text-danger'> <thead>
<h4> <tr>
{'Esta empresa no tiene administradores de dominio registrados.'} <th>{'email'}</th>
</h4> <th className='td-mbxs text-center'>{'Nombre'}</th>
</div> <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}
/>
); );
} }
return ( return <div/>;
<Panel
hasHeader={false}
children={panelBody}
/>
);
} }
} }
......
...@@ -32,7 +32,6 @@ export default class CompaniesDetails extends React.Component { ...@@ -32,7 +32,6 @@ export default class CompaniesDetails extends React.Component {
this.getCompany = this.getCompany.bind(this); this.getCompany = this.getCompany.bind(this);
this.getDomains = this.getDomains.bind(this); this.getDomains = this.getDomains.bind(this);
this.getAdmins = this.getAdmins.bind(this);
this.getPlans = this.getPlans.bind(this); this.getPlans = this.getPlans.bind(this);
} }
...@@ -76,7 +75,7 @@ export default class CompaniesDetails extends React.Component { ...@@ -76,7 +75,7 @@ export default class CompaniesDetails extends React.Component {
}, },
(data) => { (data) => {
const domains = data.domain; const domains = data.domain;
Promise.all([self.getPlans(domains), self.getAdmins(domains)]). self.getPlans(domains).
then(() => { then(() => {
company.domains = domains; company.domains = domains;
resolve(company); resolve(company);
...@@ -90,30 +89,6 @@ export default class CompaniesDetails extends React.Component { ...@@ -90,30 +89,6 @@ export default class CompaniesDetails extends React.Component {
}); });
} }
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.account;
return solve(d);
});
});
});
Promise.all(promises).
then((doms) => {
resolve(doms);
}).
catch((error) => {
reject(error);
});
});
}
getPlans(domains) { getPlans(domains) {
const names = domains.map((d) => { const names = domains.map((d) => {
return d.name; return d.name;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import React from 'react'; import React from 'react';
import UserStore from '../../stores/user_store.jsx'; import UserStore from '../../stores/user_store.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import StatusLabel from '../status_label.jsx'; import StatusLabel from '../status_label.jsx';
...@@ -21,7 +22,7 @@ export default class CompanyDomains extends React.Component { ...@@ -21,7 +22,7 @@ export default class CompanyDomains extends React.Component {
label: 'Agregar Dominio', label: 'Agregar Dominio',
props: { props: {
className: 'btn btn-default btn-xs', className: 'btn btn-default btn-xs',
onClick: (e) => Utils.handleLink(e, '/domains/new', this.props.location) onClick: (e) => Utils.handleLink(e, `/companies/${this.props.company.id}/domains/new`, this.props.location)
} }
}]; }];
} }
...@@ -47,15 +48,30 @@ export default class CompanyDomains extends React.Component { ...@@ -47,15 +48,30 @@ export default class CompanyDomains extends React.Component {
} }
let totalAccounts = 0; let totalAccounts = 0;
const plans = Utils.getPlansFromDomain(d); const cos = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
const planKeys = Object.keys(cos).map((cosKey) => {
return cos[cosKey];
});
const plans = {};
planKeys.forEach((key) => {
plans[key] = 0;
});
const domainCos = d.maxAccountsByCos();
if (domainCos) {
Object.keys(domainCos).forEach((id) => {
const limit = domainCos[id];
plans[cos[id]] += limit;
});
}
const plansArray = Object.keys(plans).map((p) => { const plansArray = planKeys.map((key) => {
const limit = plans[p].limit; const limit = plans[key];
totalAccounts += limit; totalAccounts += limit;
return ( return (
<li key={`domain-${d.id}-${p}`}> <li key={`domain-${d.id}-${key}`}>
{limit} {Utils.titleCase(p.slice(0, 3))} {limit} {Utils.titleCase(key.slice(0, 3))}
</li> </li>
); );
}); });
......
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
import React from 'react'; import React from 'react';
import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import * as Utils from '../../utils/utils.jsx'; import * as Utils from '../../utils/utils.jsx';
export default class CompanyMailboxPlans extends React.Component { export default class CompanyMailboxPlans extends React.Component {
...@@ -29,8 +32,55 @@ export default class CompanyMailboxPlans extends React.Component { ...@@ -29,8 +32,55 @@ export default class CompanyMailboxPlans extends React.Component {
]; ];
const mailboxPlans = []; const mailboxPlans = [];
const cos = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
const planKeys = Object.keys(cos).map((c) => {
return cos[c];
});
const domains = company.domains; const domains = company.domains;
const plans = Utils.getPlansFromDomains(domains);
const plans = {};
let noLimitError;
planKeys.forEach((key) => {
plans[key] = {
limit: 0,
used: 0,
free: 0,
noLimit: false
};
});
domains.forEach((d) => {
const domainCos = d.maxAccountsByCos();
const domainPlans = Utils.getPlansFromDomain(d);
let used = 0;
if (domainCos) {
Object.keys(domainCos).forEach((id) => {
const limit = domainCos[id];
used = domainPlans[cos[id]].used;
plans[cos[id]].limit += limit;
plans[cos[id]].used += used;
plans[cos[id]].free += (limit - used);
});
} else {
if (!noLimitError) {
noLimitError = (
<MessageBar
message='Existen dominios sin límites asignados'
type='WARNING'
autoclose={true}
/>
);
}
Object.keys(domainPlans).forEach((id) => {
used = domainPlans[id].used;
plans[id].noLimit = true;
plans[id].used += used;
});
}
});
for (const key in plans) { for (const key in plans) {
if (plans.hasOwnProperty(key)) { if (plans.hasOwnProperty(key)) {
...@@ -42,12 +92,12 @@ export default class CompanyMailboxPlans extends React.Component { ...@@ -42,12 +92,12 @@ export default class CompanyMailboxPlans extends React.Component {
const plan = plans[key]; const plan = plans[key];
limit = plan.limit; limit = plan.limit;
used = plan.used; used = plan.used;
if (limit) { if (plan.noLimit) {
free = limit - used;
percent = Math.round((used * 100) / limit);
} else {
limit = free = '\u221e'; limit = free = '\u221e';
percent = 100; percent = 100;
} else {
free = limit - used;
percent = Math.round((used * 100) / limit);
} }
if (percent <= 10) { if (percent <= 10) {
...@@ -112,6 +162,7 @@ export default class CompanyMailboxPlans extends React.Component { ...@@ -112,6 +162,7 @@ export default class CompanyMailboxPlans extends React.Component {
<Panel <Panel
title='Casillas' title='Casillas'
btnsHeader={headerButtons} btnsHeader={headerButtons}
error={noLimitError}
children={panelBody} children={panelBody}
/> />
); );
......
import React from 'react'; import React from 'react';
import {browserHistory} from 'react-router'; import {browserHistory} from 'react-router';
import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import Button from '../button.jsx';
import CompanyStore from '../../stores/company_store.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import ZimbraStore from '../../stores/zimbra_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';
export default class CreateDomain extends React.Component { export default class CreateDomain extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleClick = this.handleClick.bind(this); this.planSize = this.planSize.bind(this);
this.getCompanies = this.getCompanies.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = { this.state = {};
notification: false,
notificationMsg: ''
};
} }
handleClick(e, path) { planSize(e) {
e.preventDefault(); e.preventDefault();
let total = 0;
const plans = Object.keys(this.state.plans);
plans.forEach((p) => {
total += parseInt(this.refs[`plan-${p}`].value, 10) || 0;
});
this.refs.mailboxLimit.value = total;
}
browserHistory.push(path); getCompanies() {
const companyId = this.props.params.id;
const companies = CompanyStore.getCompanies();
if (companies) {
this.setState({
plans: Utils.getEnabledPlansByCos(ZimbraStore.getAllCos()),
companies,
companyId
});
return GlobalActions.emitEndLoading();
}
return Client.getAllCompanies().
then((data) => {
this.setState({
plans: Utils.getEnabledPlansByCos(ZimbraStore.getAllCos()),
companies: data,
companyId
});
}).
catch((error) => {
error.type = Constants.MessageType.ERROR;
this.setState({error});
}).
finally(() => {
GlobalActions.emitEndLoading();
});
}
handleSubmit(e) {
e.preventDefault();
GlobalActions.emitStartLoading();
const elementList = document.querySelectorAll('.has-error');
Array.from(elementList).forEach((el) => el.classList.remove('has-error'));
Utils.validateInputRequired(this.refs).
then(() => {
const plans = Object.keys(this.state.plans);
const zimbraDomainCOSMaxAccounts = [];
const name = this.refs.domainName.value.trim();
const businessCategory = this.refs.company.value.trim();
plans.forEach((p) => {
zimbraDomainCOSMaxAccounts.push(`${this.refs[`plan-${p}`].getAttribute('data-id')}:${this.refs[`plan-${p}`].value || 0}`);
});
const domain = {
name,
attrs: {
zimbraDomainCOSMaxAccounts,
businessCategory
}
};
Client.createDomain(
domain,
(data) => {
CompanyStore.addDomain(businessCategory, data);
DomainStore.setCurrent(data);
browserHistory.push(`/domains/${data.id}`);
},
(error) => {
GlobalActions.emitEndLoading();
return this.setState({error});
}
);
}).
catch((error) => {
GlobalActions.emitEndLoading();
error.refs = true;
error.type = error.typeError;
error.node.closest('.form-group').classList.add('has-error');
return this.setState({error});
});
}
componentDidMount() {
this.getCompanies();
} }
render() { render() {
let form = ( const companies = this.state.companies;
<form className='simple_form form-horizontal mailbox-form'> const error = this.state.error;
<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='domainName'
placeholder='example.com'
/>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'Cuenta'}
</label>
<div className='col-sm-8'>
<select
className='form-control select required'
required='required'
ref='account'
>
</select> if (companies || error) {
</div> let backUrl = '/domains';
</div> if (this.state.companyId) {
backUrl = `/companies/${this.state.companyId}`;
<div className='form-group string'> }
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr> let errorBar;
{'Equipo Chat'} if (error) {
</label> errorBar = (
<MessageBar
<div className='col-sm-8'> message={error.message}
<input type={error.type}
type='text' autoclose={true}
className='form-control' />
ref='chatTeam' );
placeholder='Nombre (solo letras o números, no guiones bajos)' }
/>
</div>
</div>
<div className='form-group string'> const companiesOptions = companies.map((c) => {
<label className='string required col-sm-3 control-label'> return (
{'Renovación Anual'} <option
</label> key={`company-${c.id}`}
value={c.id}
>
{c.name}
</option>
);
});
<div className='col-sm-8'> const statePlans = this.state.plans;
<label className='radio-inline pretty-input'> const enabledPlans = Object.keys(statePlans);
<div className='pretty-checkbox'> const plans = enabledPlans.map((p) => {
return (
<div
key={`plan-${statePlans[p]}`}
className='col-md-4 form-group required'
>
<label
htmlFor={`plan-${p}`}
clasName='label-top control-label'
>
<abbr title='Requerido'>{'*'}</abbr><br/>
{Utils.titleCase(p)}
</label>
<br/>
<div className='row'>
<div className='col-sm-8'>
<input <input
type='checkbox' type='text'
className='pretty' className='form-control'
ref='annualRenovation' defaultValue='0'
data-required='true'
data-message={`Debe asignar la cantidad de casillas del tipo ${Utils.titleCase(p)}`}
data-id={statePlans[p]}
ref={`plan-${p}`}
onKeyUp={this.planSize}
/> />
<span></span>
</div> </div>
</div>
</div>
);
});
const form = (
<form
className='simple_form form-horizontal mailbox-form'
onSubmit={this.handleSubmit}
>
<div className='form-group string required'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'Nombre'}
</label> </label>
<div className='col-sm-8'>
<input
type='text'
data-required='true'
data-message='El nombre del dominio es obligatorio'
className='form-control'
ref='domainName'
placeholder='example.com'
/>
</div>
</div> </div>
</div>
<div className='form-group row'>
<div className='col-md-8 col-md-offset-3'>
<div className='box-content'>
<div className='col-md-4 form-group required'>
<label
htmlFor='domain_mbx_basic_limit'
clasName='label-top control-label'
>
<abbr title='Requerido'>{'*'}</abbr><br/>
{'Básica'}
</label>
<br/>
<div className='row'>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
defaulValue='0'
ref='plan_basica'
/>
</div>
</div>
</div>
<div className='col-md-4 form-group required'> <div className='form-group string'>
<label <label className='string required col-sm-3 control-label'>
htmlFor='domain_mbx_basic_limit' <abbr title='requerido'>{'*'}</abbr>
clasName='label-top control-label' {'Empresa'}
> </label>
<abbr title='Requerido'>{'*'}</abbr><br/>
{'Profesional'}
</label>
<br/>
<div className='row'>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
defaulValue='0'
ref='plan_professional'
/>
</div>
</div>
</div>
<div className='col-md-4 form-group required'> <div className='col-sm-8'>
<label <select
htmlFor='domain_mbx_basic_limit' className='form-control select required'
clasName='label-top control-label' data-required='true'
> data-message='Debe especificar a que empresa corresponde el dominio'
<abbr title='Requerido'>{'*'}</abbr><br/> ref='company'
{'Premium'} defaultValue={this.state.companyId}
</label> >
{companiesOptions}
<br/> </select>
</div>
<div className='row'> </div>
<div className='col-sm-8'>
<input <div className='form-group row'>
type='text' <div className='col-md-8 col-md-offset-3'>
className='form-control' <div className='box-content'>
defaulValue='0' {plans}
ref='plan_premium'
/>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div className='form-group string'>
<div className='form-group string'> <label className='string required col-sm-3 control-label'>
<label className='string required col-sm-3 control-label'> {'Límite de casillas'}
{'Límite de casillas'} </label>
</label>
<div className='col-sm-8'>
<div className='col-sm-8'> <input
<input type='text'
type='text' className='form-control'
className='form-control' ref='mailboxLimit'
ref='mailboxLimit' value='0'
defaulValue='0' disabled='disabled'
disabled='disabled' />
/> </div>
</div> </div>
</div>
<div className='form-group'>
<div className='form-group'> <div className='col-sm-8 col-sm-offset-3'>
<div className='col-sm-8 col-sm-offset-3'> <input
<input type='submit'
type='submit' name='commit'
name='commit' value='Guardar'
defaulValue='Guardar' className='btn btn-info'
className='btn btn-info' />
/> <a
<Button href='#'
btnAttrs={ className='btn btn-default'
{ onClick={(e) => Utils.handleLink(e, backUrl)}
className: 'btn btn-default', >
onclick: (e) => { {'Cancelar'}
this.handleClick(e, '/domains'); </a>
} </div>
}
}
>
{'Cancelar'}
</Button>
</div> </div>
</div> </form>
</form> );
);
const actions = [
const actions = [ {
{ label: 'Cancelar',
label: 'Cancelar', props: {
props: { className: 'btn btn-default btn-xs',
className: 'btn btn-default btn-xs', onClick: (e) => Utils.handleLink(e, backUrl)
onclick: (e) => {
this.handleClick(e, '/domains');
} }
} }
} ];
];
return (
return ( <Panel
<Panel title={'Agregar Dominio'}
title={'Agregar Dominio'} btnsHeader={actions}
btnsHeader={actions} error={errorBar}
classHeader={'forum-box'} classHeader={'forum-box'}
> >
{form} {form}
</Panel> </Panel>
); );
}
return <div/>;
} }
} }
CreateDomain.propTypes = {
params: React.PropTypes.object.isRequired
};
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
import React from 'react'; import React from 'react';
import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import * as Utils from '../../utils/utils.jsx'; import * as Utils from '../../utils/utils.jsx';
export default class DomainMailboxPlans extends React.Component { export default class DomainMailboxPlans extends React.Component {
...@@ -61,15 +64,56 @@ export default class DomainMailboxPlans extends React.Component { ...@@ -61,15 +64,56 @@ export default class DomainMailboxPlans extends React.Component {
]; ];
const mailboxPlans = []; const mailboxPlans = [];
const plans = this.state.plans; const cos = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
const configPlans = global.window.manager_config.plans; const planKeys = Object.keys(cos).map((c) => {
return cos[c];
});
const domain = this.props.domain;
const domainCos = domain.maxAccountsByCos();
const domainPlans = Utils.getPlansFromDomain(domain);
const plans = {};
let noLimitError;
let totalUsed = 0; let totalUsed = 0;
let totalLimit = 0; let totalLimit = 0;
planKeys.forEach((key) => {
plans[key] = {
limit: 0,
used: 0
};
});
let used = 0;
if (domainCos) {
Object.keys(domainCos).forEach((id) => {
const limit = domainCos[id];
used = domainPlans[cos[id]].used;
plans[cos[id]].limit += limit;
plans[cos[id]].used += used;
});
}
for (const key in plans) { for (const key in plans) {
if (plans.hasOwnProperty(key) && configPlans[key]) { if (plans.hasOwnProperty(key)) {
const plan = plans[key]; const plan = plans[key];
totalUsed += plan.used; totalUsed += plan.used;
totalLimit += plan.limit || 0; if (plan.limit === 0) {
totalLimit = '\u221e';
if (!noLimitError) {
noLimitError = (
<MessageBar
message='Existen dominios sin límites asignados'
type='WARNING'
autoclose={true}
/>
);
}
} else {
totalLimit += plan.limit;
}
mailboxPlans.push( mailboxPlans.push(
<tr key={`plan-${key}`}> <tr key={`plan-${key}`}>
<td className='mbx-plan' <td className='mbx-plan'
...@@ -105,7 +149,7 @@ export default class DomainMailboxPlans extends React.Component { ...@@ -105,7 +149,7 @@ export default class DomainMailboxPlans extends React.Component {
className='text-center' className='text-center'
style={{borderTop: 0}} style={{borderTop: 0}}
> >
<strong>{totalLimit || '\u221e'}</strong> <strong>{totalLimit}</strong>
</td> </td>
<td <td
className='text-center' className='text-center'
...@@ -141,6 +185,7 @@ export default class DomainMailboxPlans extends React.Component { ...@@ -141,6 +185,7 @@ export default class DomainMailboxPlans extends React.Component {
<Panel <Panel
title='Casillas' title='Casillas'
btnsHeader={headerButtons} btnsHeader={headerButtons}
error={noLimitError}
children={panelBody} children={panelBody}
/> />
); );
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import $ from 'jquery'; import $ from 'jquery';
import React from 'react'; import React from 'react';
import Promise from 'bluebird';
import MessageBar from '../message_bar.jsx'; import MessageBar from '../message_bar.jsx';
import PageInfo from '../page_info.jsx'; import PageInfo from '../page_info.jsx';
...@@ -42,42 +43,51 @@ export default class Domains extends React.Component { ...@@ -42,42 +43,51 @@ export default class Domains extends React.Component {
}, },
(data) => { (data) => {
const domains = data.domain; const domains = data.domain;
const plans = domains.map((d) => { this.getPlans(domains).
return self.getPlans(d); then(() => {
self.setState({
data
});
}).
catch(() => {
this.setState({
error: {
message: 'No se obtuvieron los planes de las cuentas',
type: messageType.ERROR
}
});
}).
finally(() => {
GlobalActions.emitEndLoading();
}); });
Promise.all(plans).then(
() => {
self.setState({
data
});
GlobalActions.emitEndLoading();
},
() => {
this.setState({
error: 'No se obtuvieron los planes de las cuentas'
});
GlobalActions.emitEndLoading();
}
);
}, },
(error) => { (error) => {
this.setState({ this.setState({
error: error.message error: {
message: error.message,
type: messageType.ERROR
}
}); });
GlobalActions.emitEndLoading(); GlobalActions.emitEndLoading();
} }
); );
} }
getPlans(domain) { getPlans(domains) {
const names = domains.map((d) => {
return d.name;
});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Client.countAccounts(domain.name, return Client.batchCountAccount(
(info) => { names,
domain.plans = info; (data) => {
resolve(); domains.forEach((d, i) => {
d.plans = data[i];
});
resolve(domains);
}, },
() => { (error) => {
reject(); reject(error);
} }
); );
}); });
...@@ -105,12 +115,13 @@ export default class Domains extends React.Component { ...@@ -105,12 +115,13 @@ export default class Domains extends React.Component {
} }
render() { render() {
const error = this.state.error;
let message; let message;
if (this.state.error) { if (error) {
message = ( message = (
<MessageBar <MessageBar
message={this.state.error} message={error.message}
type={messageType.WARNING} type={error.type}
autoclose={true} autoclose={true}
/> />
); );
......
import React from 'react'; import React from 'react';
import Textarea from 'react-textarea-autosize';
import {browserHistory} from 'react-router'; import {browserHistory} from 'react-router';
import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx'; import Panel from '../panel.jsx';
import Button from '../button.jsx';
import CompanyStore from '../../stores/company_store.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import Constants from '../../utils/constants.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
export default class EditDomain extends React.Component { export default class EditDomain extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleClick = this.handleClick.bind(this); this.getDomain = this.getDomain.bind(this);
this.getCompanies = this.getCompanies.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {};
}
getDomain() {
const domainId = this.props.params.id;
const domain = DomainStore.getCurrent();
if (domain && domain.id === domainId) {
return this.getCompanies(domain);
}
return Client.getDomain(
domainId,
(data) => {
this.getCompanies(data);
},
(error) => {
GlobalActions.emitEndLoading();
this.setState({error});
}
);
}
getCompanies(domain) {
const companies = CompanyStore.getCompanies();
if (companies) {
this.setState({
domain,
companies
});
return GlobalActions.emitEndLoading();
}
this.state = { return Client.getAllCompanies().
notification: false, then((data) => {
notificationMsg: '' this.setState({
}; domain,
companies: data
});
GlobalActions.emitEndLoading();
}).
catch((error) => {
error.type = Constants.MessageType.ERROR;
this.setState({error});
GlobalActions.emitEndLoading();
});
} }
handleClick(e, path) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
GlobalActions.emitStartLoading();
browserHistory.push(path); const elementList = document.querySelectorAll('.has-error');
Array.from(elementList).forEach((el) => el.classList.remove('has-error'));
Utils.validateInputRequired(this.refs).
then(() => {
const stateDomain = this.state.domain;
const id = stateDomain.id;
const prevCompanyId = stateDomain.attrs.businessCategory;
const businessCategory = this.refs.company.value.trim();
const description = this.refs.description.value.trim();
const zimbraNotes = this.refs.notes.value.trim();
const domain = {
id,
attrs: {
businessCategory,
description,
zimbraNotes
}
};
Client.modifyDomain(
domain,
(data) => {
CompanyStore.modifyDomain(prevCompanyId, data);
DomainStore.setCurrent(data);
browserHistory.push(`/domains/${data.id}`);
},
(error) => {
GlobalActions.emitEndLoading();
return this.setState({error});
}
);
}).
catch((error) => {
GlobalActions.emitEndLoading();
error.refs = true;
error.type = error.typeError;
if (error.node) {
error.node.closest('.form-group').classList.add('has-error');
}
return this.setState({error});
});
}
componentDidMount() {
this.getDomain();
} }
render() { render() {
let form = ( const domain = this.state.domain;
<form className='simple_form form-horizontal mailbox-form'> const error = this.state.error;
<div className='form-group string required'>
<label className='string required col-sm-3 control-label'> if (domain || error) {
<abbr title='requerido'>{'*'}</abbr> const companies = this.state.companies;
{'Nombre'} const companiesOptions = companies.map((c) => {
</label> return (
<option
<div className='col-sm-8'> key={`company-${c.id}`}
<input value={c.id}
type='text' >
required='required' {c.name}
className='form-control' </option>
ref='domainName' );
value='dominio.com' });
disabled='disabled'
/> let messageBar;
</div> if (error) {
</div> messageBar = (
<MessageBar
message={error.message}
type={error.type}
autoclose={true}
/>
);
}
let lastRenovation;
// DESCOMENTAR ESTO Y CREAR LA FUNCIONALIDAD PARA MANEJAR LA ÚLTIMA FECHA DE RENOVACIÓN
// <div className='form-group string'>
// <label className='string required col-sm-3 control-label'>
// {'Última Renovación'}
// </label>
//
// <div className='col-sm-8'>
// <div className='input-group date datetimepicker'>
// <input
// type='text'
// className='form-control'
// ref='lastRenovation'
// />
// <span className='input-group-btn'>
// <button
// className='btn btn-default'
// type='button'
// >
// <span className='fa fa-calendar'></span>
// </button>
// </span>
// </div>
// </div>
// </div>
<div className='form-group string'> const form = (
<label className='string required col-sm-3 control-label'> <form
{'Última Renovación'} className='simple_form form-horizontal mailbox-form'
</label> onSubmit={this.handleSubmit}
>
<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'> <div className='col-sm-8'>
<div className='input-group date datetimepicker'>
<input <input
type='text' type='text'
className='form-control' className='form-control'
ref='lastRenovation' ref='domainName'
value={domain.name}
disabled='disabled'
/> />
<span className='input-group-btn'>
<button
className='btn btn-default'
type='button'
>
<span className='fa fa-calendar'></span>
</button>
</span>
</div> </div>
</div> </div>
</div>
{lastRenovation}
<div className='form-group string'>
<label className='string required col-sm-3 control-label'> <div className='form-group string'>
<abbr title='requerido'>{'*'}</abbr> <label className='string required col-sm-3 control-label'>
{'Cuenta'} <abbr title='requerido'>{'*'}</abbr>
</label> {'Empresa'}
</label>
<div className='col-sm-8'>
<select <div className='col-sm-8'>
className='form-control select required' <select
required='required' className='form-control select required'
ref='account' data-required='true'
> data-message='Debe especificar a que empresa corresponde el dominio'
ref='company'
</select> defaultValue={domain.attrs.businessCategory}
>
{companiesOptions}
</select>
</div>
</div> </div>
</div>
<div className='form-group string'>
<div className='form-group string'> <label className='string col-sm-3 control-label'>
<label className='string required col-sm-3 control-label'> {'Descripción'}
<abbr title='requerido'>{'*'}</abbr> </label>
{'Equipo Chat'}
</label> <div className='col-sm-8'>
<input
<div className='col-sm-8'> type='text'
<input className='form-control'
type='text' ref='description'
className='form-control' placeholder='Descripción del dominio'
ref='chatTeam' defaultValue={domain.attrs.description}
placeholder='Nombre (solo letras o números, no guiones bajos)' />
/> </div>
</div> </div>
</div>
<div className='form-group string'>
<div className='form-group string'> <label className='string col-sm-3 control-label'>
<label className='string required col-sm-3 control-label'> {'Notas'}
{'Renovación Anual'}
</label>
<div className='col-sm-8'>
<label className='radio-inline pretty-input'>
<div className='pretty-checkbox'>
<input
type='checkbox'
className='pretty'
ref='autoRenovation'
/>
<span></span>
</div>
</label> </label>
<div className='col-sm-8'>
<Textarea
className='form-control'
ref='notes'
defaultValue={domain.attrs.zimbraNotes}
placeholder='Notas para el dominio'
minRows={3}
maxRows={9}
/>
</div>
</div> </div>
</div>
<div className='form-group'>
<div className='form-group'> <div className='col-sm-8 col-sm-offset-3'>
<div className='col-sm-8 col-sm-offset-3'> <input
<input type='submit'
type='submit' name='commit'
name='commit' value='Guardar'
value='Guardar' className='btn btn-info'
className='btn btn-info' />
/> <a
<Button href='#'
btnAttrs={ className='btn btn-default'
{ onClick={(e) => Utils.handleLink(e, `/domains/${domain.id}`)}
className: 'btn btn-default', >
onClick: (e) => { {'Cancelar'}
this.handleClick(e, '/domains'); </a>
} </div>
}
}
>
{'Cancelar'}
</Button>
</div> </div>
</div> </form>
</form> );
);
const actions = [ const actions = [
{ {
label: 'Cancelar', label: 'Cancelar',
props: { props: {
className: 'btn btn-default btn-xs', className: 'btn btn-default btn-xs',
onClick: (e) => { onClick: (e) => Utils.handleLink(e, `/domains/${domain.id}`)
this.handleClick(e, '/domains');
} }
} }
} ];
];
return (
return ( <Panel
<Panel title={'Editar Dominio'}
title={'Editar Dominio'} btnsHeader={actions}
btnsHeader={actions} classHeader={'forum-box'}
classHeader={'forum-box'} error={messageBar}
> >
{form} {form}
</Panel> </Panel>
); );
}
return <div/>;
} }
} }
EditDomain.propTypes = {
params: React.PropTypes.object.isRequired
};
...@@ -6,6 +6,8 @@ import React from 'react'; ...@@ -6,6 +6,8 @@ import React from 'react';
import {browserHistory} from 'react-router'; import {browserHistory} from 'react-router';
import UserStore from '../../stores/user_store.jsx'; import UserStore from '../../stores/user_store.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import Constants from '../../utils/constants.jsx'; import Constants from '../../utils/constants.jsx';
import * as Client from '../../utils/client.jsx'; import * as Client from '../../utils/client.jsx';
...@@ -66,7 +68,12 @@ export default class Login extends React.Component { ...@@ -66,7 +68,12 @@ export default class Login extends React.Component {
Client.login(email, password, Client.login(email, password,
() => { () => {
browserHistory.push('/companies'); return Client.getAllCos(
(cosData) => {
ZimbraStore.setAllCos(cosData);
browserHistory.push('/companies');
}
);
}, },
(err) => { (err) => {
this.setState({loginError: err.message}); this.setState({loginError: err.message});
......
...@@ -103,7 +103,7 @@ export default class MessageBar extends React.Component { ...@@ -103,7 +103,7 @@ export default class MessageBar extends React.Component {
MessageBar.defaultProps = { MessageBar.defaultProps = {
message: null, message: null,
type: 'error', type: 'ERROR',
position: 'relative', position: 'relative',
canClose: true, canClose: true,
autoclose: false, autoclose: false,
......
...@@ -77,7 +77,17 @@ function onPreLoggedIn(nextState, replace, callback) { ...@@ -77,7 +77,17 @@ function onPreLoggedIn(nextState, replace, callback) {
global.window.Zimbra = ZimbraStore.getCurrent(); global.window.Zimbra = ZimbraStore.getCurrent();
} }
return callback(); const cos = ZimbraStore.getAllCos();
if (cos) {
return callback();
}
return Client.getAllCos(
(cosData) => {
ZimbraStore.setAllCos(cosData);
return callback();
}
);
}, },
(err) => { (err) => {
let query; let query;
...@@ -145,6 +155,10 @@ function renderRootComponent() { ...@@ -145,6 +155,10 @@ function renderRootComponent() {
path='companies/:id' path='companies/:id'
component={Company} component={Company}
/> />
<Route
path='companies/:id/domains/new'
component={CreateDomains}
/>
<Route <Route
path='mailboxes' path='mailboxes'
......
...@@ -51,6 +51,88 @@ class CompanyStoreClass { ...@@ -51,6 +51,88 @@ class CompanyStoreClass {
setCurrent(company) { setCurrent(company) {
this.current = company; this.current = company;
} }
addDomain(companyId, domain) {
const currentCompany = this.getCurrent();
const company = this.getCompanyById(companyId);
if (currentCompany && currentCompany.id === companyId) {
currentCompany.domains.push(domain);
if (company) {
this.companies[companyId] = currentCompany;
}
} else if (company) {
company.domains.push(domain);
}
}
modifyDomain(prevCompanyId, domain) {
if (this.companies) {
const domainCompanyId = domain.attrs.businessCategory;
const prevCompany = this.companies[prevCompanyId];
const newCompany = this.companies[domainCompanyId];
if (prevCompanyId !== domainCompanyId) {
if (newCompany) {
this.addDomain(newCompany.id, domain);
}
if (prevCompany) {
const index = findDomain(prevCompany, domain);
prevCompany.domains.splice(index, 1);
}
} else if (prevCompany) {
const index = findDomain(prevCompany, domain);
replaceDomain(prevCompany, domain, index);
}
}
}
addDomainAdmins(companyId, domain) {
const currentCompany = this.getCurrent();
const company = this.getCompanyById(companyId);
let index = -1;
if (currentCompany && currentCompany.id === companyId) {
index = findDomain(currentCompany, domain);
replaceDomain(currentCompany, domain, index);
if (company) {
this.companies[companyId] = currentCompany;
}
} else if (company) {
index = findDomain(company, domain);
replaceDomain(company, domain, index);
}
}
}
function findDomain(company, domain) {
const domains = company.domains;
let index = -1;
if (domains) {
domains.forEach((d, i) => {
if (d.id === domain.id) {
index = i;
return false;
}
return true;
});
}
return index;
}
function replaceDomain(company, domain, index) {
if (index >= 0) {
company.domains[index] = domain;
} else {
company.domains.push(domain);
}
} }
const CompanyStore = new CompanyStoreClass(); const CompanyStore = new CompanyStoreClass();
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
class ZimbraStoreClass { class ZimbraStoreClass {
constructor() { constructor() {
this.zimbra = null; this.zimbra = null;
this.cos = null;
} }
getCurrent() { getCurrent() {
...@@ -13,6 +14,14 @@ class ZimbraStoreClass { ...@@ -13,6 +14,14 @@ class ZimbraStoreClass {
setCurrent(zimbra) { setCurrent(zimbra) {
this.zimbra = zimbra; this.zimbra = zimbra;
} }
setAllCos(cos) {
this.cos = cos;
}
getAllCos() {
return this.cos;
}
} }
var ZimbraStore = new ZimbraStoreClass(); var ZimbraStore = new ZimbraStoreClass();
......
...@@ -228,6 +228,44 @@ export function getDomain(id, success, error) { ...@@ -228,6 +228,44 @@ export function getDomain(id, success, error) {
); );
} }
export function createDomain(domain, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.createDomain(domain.name, domain.attrs, (err, data) => {
if (err) {
const e = handleError('createDomain', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('createDomain', err);
return error(e);
}
);
}
export function modifyDomain(domain, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.modifyDomain(domain.id, domain.attrs, (err, data) => {
if (err) {
const e = handleError('modifyDomain', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('modifyDomain', err);
return error(e);
}
);
}
export function addDistributionList(name, attrs, success, error) { export function addDistributionList(name, attrs, success, error) {
initZimbra().then( initZimbra().then(
(zimbra) => { (zimbra) => {
...@@ -500,7 +538,9 @@ export function getAllCos(success, error) { ...@@ -500,7 +538,9 @@ export function getAllCos(success, error) {
zimbra.getAllCos((err, data) => { zimbra.getAllCos((err, data) => {
if (err) { if (err) {
const e = handleError('getAllCos', err); const e = handleError('getAllCos', err);
return error(e); if (error) {
return error(e);
}
} }
return success(data); return success(data);
...@@ -508,7 +548,11 @@ export function getAllCos(success, error) { ...@@ -508,7 +548,11 @@ export function getAllCos(success, error) {
}, },
(err) => { (err) => {
const e = handleError('getAllCos', err); const e = handleError('getAllCos', err);
return error(e); if (error) {
return error(e);
}
return null;
} }
); );
} }
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
import {browserHistory} from 'react-router'; import {browserHistory} from 'react-router';
import * as GlobalActions from '../action_creators/global_actions.jsx'; import * as GlobalActions from '../action_creators/global_actions.jsx';
import CONSTANTS from './constants.jsx'; import Constants from './constants.jsx';
const messageType = Constants.MessageType;
export function setCookie(cname, cvalue) { export function setCookie(cname, cvalue) {
localStorage.setItem(cname, cvalue); localStorage.setItem(cname, cvalue);
...@@ -168,7 +170,7 @@ export function validateInputRequired(refs) { ...@@ -168,7 +170,7 @@ export function validateInputRequired(refs) {
} }
for (const ref in refs) { for (const ref in refs) {
if (refs.hasOwnProperty(ref)) { if (refs.hasOwnProperty(ref) && refs[ref].hasAttribute) {
if (refs[ref].hasAttribute('data-required') && refs[ref].getAttribute('data-required') === 'true' && refs[ref].value === '') { if (refs[ref].hasAttribute('data-required') && refs[ref].getAttribute('data-required') === 'true' && refs[ref].value === '') {
let message; let message;
if (refs[ref].getAttribute('data-message') && refs[ref].getAttribute('data-message').length > 0) { if (refs[ref].getAttribute('data-message') && refs[ref].getAttribute('data-message').length > 0) {
...@@ -178,7 +180,7 @@ export function validateInputRequired(refs) { ...@@ -178,7 +180,7 @@ export function validateInputRequired(refs) {
} }
const Error = { const Error = {
message, message,
typeError: 'warning', typeError: messageType.ERROR,
node: refs[ref] node: refs[ref]
}; };
...@@ -210,7 +212,7 @@ export function dateFormatted(dateString, isShortDate, separator) { ...@@ -210,7 +212,7 @@ export function dateFormatted(dateString, isShortDate, separator) {
const year = date.substr(0, 4); const year = date.substr(0, 4);
const month = (isShortDate) ? date.substr(4, 2) : parseInt(date.substr(4, 2), 10); const month = (isShortDate) ? date.substr(4, 2) : parseInt(date.substr(4, 2), 10);
const day = date.substr(6, 2); const day = date.substr(6, 2);
let dateFormattedString = `${day} de ${CONSTANTS.MONTHS[month - 1]} de ${year}`; let dateFormattedString = `${day} de ${Constants.MONTHS[month - 1]} de ${year}`;
if (isShortDate) { if (isShortDate) {
dateFormattedString = `${day}${separator}${month}${separator}${year}`; dateFormattedString = `${day}${separator}${month}${separator}${year}`;
...@@ -295,3 +297,17 @@ export function getEnabledPlansByCos(cosArray) { ...@@ -295,3 +297,17 @@ export function getEnabledPlansByCos(cosArray) {
return plans; return plans;
} }
export function getEnabledPlansByCosId(cosArray) {
const configPlans = global.window.manager_config.plans;
const plans = {};
cosArray.forEach((cos) => {
const key = cos.name;
if (configPlans.hasOwnProperty(key) && configPlans[key]) {
plans[cos.id] = key;
}
});
return plans;
}
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