Commit 91a2ec23 authored by Patricio Bruna's avatar Patricio Bruna

Merge pull request #42 from ZBoxApp/distribution_lists

apply store in distribution list and mailbox, and add, edit, remove, distribution list, add pagination mailbox
parents 33df3db4 f190b8af
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import $ from 'jquery';
import React from 'react';
import EventStore from '../../stores/event_store.jsx';
import PageInfo from '../page_info.jsx';
import PanelTab from '../panel_tab.jsx';
import Button from '../button.jsx';
import Panel from '../panel.jsx';
import ActionsPanel from '../panel_actions.jsx';
import ActionsPanelAllows from '../panel_actions_allows.jsx';
import MessageBar from '../message_bar.jsx';
import Promise from 'bluebird';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import Constants from '../../utils/constants.jsx';
const MessagesType = Constants.MessageType;
export default class DistributionLists extends React.Component {
constructor(props) {
super(props);
this.getDistributionLists = this.getDistributionLists.bind(this);
this.showMessage = this.showMessage.bind(this);
this.onDeleteMember = this.onDeleteMember.bind(this);
this.onDeleteOwner = this.onDeleteOwner.bind(this);
this.onSubmitMembers = this.onSubmitMembers.bind(this);
this.onCancelMember = this.onCancelMember.bind(this);
this.onCancelOwner = this.onCancelOwner.bind(this);
this.domain = null;
this.state = {};
}
showMessage(attrs) {
const autoTimer = 10;
this.setState({
error: attrs.error,
type: attrs.typeError,
autocloseInSecs: autoTimer
});
}
getDistributionLists() {
const id = this.props.params.id;
const response = {};
const domain = DomainStore.getCurrent();
return new Promise((resolve, reject) => {
if (domain) {
response.domain = domain;
const dl = DomainStore.getDistributionListById(id, domain);
response.distributionsList = dl;
dl.getOwners((error, owners) => {
if (owners) {
response.owners = owners;
return resolve(response);
}
return reject(error);
});
}
return Client.getDistributionList(id, (success) => {
const distributionsList = success;
response.distributionsList = distributionsList;
const domainId = this.props.params.domain_id;
distributionsList.getOwners((error, owners) => {
if (owners) {
response.owners = owners;
Client.getDomain(domainId, (doma) => {
response.domain = doma;
resolve(response);
}, (err) => {
reject(err);
});
}
});
}, (error) => {
reject(error);
});
}).then((data) => {
DomainStore.setOwners(Utils.getOwners(data.owners));
DomainStore.setMembers(data.distributionsList.members);
this.setState({
distributionsList: data.distributionsList,
members: DomainStore.getMembers(),
owners: DomainStore.getOwners(),
domain: data.domain
});
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: MessagesType.ERROR
});
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
onSubmitOwners(response) {
if (response.refresh) {
response.refresh.forEach((member) => {
DomainStore.addOwners(member);
});
}
this.multipleActionsOwners(response, this.state.distributionsList).then((res) => {
const newOwners = DomainStore.getOwners();
const errors = [];
const limit = res.length;
for (let i = 0; i < limit; i++) {
const items = res[i];
if (items.error) {
const action = (items.action === 'remove') ? 'eliminar' : 'agregar';
errors.push({
error: `Hubo un error al ${action} ${items.item}, debido a : ${items.error.extra.reason}`,
type: MessagesType.ERROR
});
}
}
if (errors.length !== limit) {
errors.push({
error: 'Se han guardado los datos para permitidos éxitosamente.',
type: MessagesType.SUCCESS
});
}
this.setState({
owners: newOwners,
error: errors
});
response.reset();
});
}
onSubmitMembers(response) {
if (response.refresh) {
response.refresh.forEach((member) => {
DomainStore.addMember(member);
});
}
this.multipleActionsMembers(response, this.state.distributionsList).then((res) => {
const newMembers = DomainStore.getMembers();
const errors = [];
const limit = res.length;
for (let i = 0; i < limit; i++) {
const items = res[i];
if (items.error) {
const action = (items.action === 'remove') ? 'eliminar' : 'agregar';
errors.push({
error: `Hubo un error al ${action} ${items.item}, debido a : ${items.error.extra.reason}`,
type: MessagesType.ERROR
});
}
}
if (errors.length !== limit) {
errors.push({
error: 'Se han guardado los datos para miembros éxitosamente.',
type: MessagesType.SUCCESS
});
}
this.setState({
members: newMembers,
error: errors
});
response.reset();
});
}
multipleActionsMembers(response, account) {
const promises = [];
if (response.add || response.remove) {
if (response.add) {
const add = new Promise((resolve) => {
account.addMembers(response.add, (error) => {
const res = {};
if (error) {
res.isCompleted = false;
res.action = 'add';
res.error = error;
return resolve(res);
}
response.add.forEach((member) => {
DomainStore.addMember(member);
});
res.isCompleted = true;
res.action = 'add';
return resolve(res);
});
});
promises.push(add);
}
if (response.remove) {
const remove = new Promise((resolve) => {
account.removeMembers(response.remove, (error) => {
const res = {};
if (error) {
res.isCompleted = false;
res.action = 'remove';
res.error = error;
return resolve(res);
}
response.remove.forEach((member) => {
DomainStore.removeMember(member);
});
res.isCompleted = true;
res.action = 'remove';
return resolve(res);
});
});
promises.push(remove);
}
}
return Promise.all(promises);
}
multipleActionsOwners(response, account) {
const promises = [];
for (const key in response) {
if (response.hasOwnProperty(key) && key === 'add') {
const array = response[key];
const limit = array.length;
for (let index = 0; index < limit; index++) {
const res = {};
const promesa = new Promise((resolve) => {
account.addOwner(array[index], (error) => {
if (error) {
res.isCompleted = false;
res.item = response[key][index];
res.action = key;
res.error = error;
} else {
res.isCompleted = true;
res.item = response[key][index];
res.action = key;
DomainStore.addOwners(response[key][index]);
}
return resolve(res);
});
});
promises.push(promesa);
}
}
if (response.hasOwnProperty(key) && key === 'remove') {
const array = response[key];
const limit = array.length;
for (let index = 0; index < limit; index++) {
const res = {};
const promesa = new Promise((resolve) => {
account.removeOwner(array[index], (error) => {
if (error) {
res.isCompleted = false;
res.item = response[key][index];
res.action = key;
res.error = error;
} else {
res.isCompleted = true;
res.item = response[key][index];
res.action = key;
}
return resolve(res);
});
});
promises.push(promesa);
}
}
}
return Promise.all(promises);
}
onDeleteMember(member) {
DomainStore.removeMember(member);
const currentMembers = DomainStore.getMembers();
this.setState({
members: currentMembers,
error: false
});
}
onCancelMember(response) {
if (response && response.length > 0) {
response.forEach((member) => {
DomainStore.addMember(member);
});
const newMembers = DomainStore.getMembers();
this.setState({
members: newMembers,
error: false
});
}
return null;
}
onDeleteOwner(owner) {
DomainStore.removeOwner(owner);
const currentOwners = DomainStore.getOwners();
this.setState({
owners: currentOwners,
error: false
});
}
onCancelOwner(response) {
if (response && response.length > 0) {
response.forEach((member) => {
DomainStore.addOwners(member);
});
const newOwners = DomainStore.getOwners();
this.setState({
owners: newOwners,
error: false
});
}
return null;
}
componentDidMount() {
EventStore.addMessageListener(this.showMessage);
$('#sidebar-domains').addClass('active');
this.getDistributionLists();
}
componentWillUnmount() {
EventStore.removeMessageListener(this.showMessage);
$('#sidebar-domains').removeClass('active');
}
render() {
let generalData;
let domainInfo;
let btnsGeneralInfo = [];
let actionTabsAllows = [];
let actionTabsMembers = [];
let message;
let panelTabs;
let isPrivate = null;
if (this.state.distributionsList && this.state.owners) {
const data = this.state.distributionsList;
const domain = this.state.domain;
const owners = this.state.owners;
const arrayMembers = this.state.members;
const membersFormatted = Utils.getMembers(data.members, 'Miembros');
if (owners.length > 0) {
isPrivate = (
<span className='label label-danger'>
{'Privada'}
</span>
);
}
generalData = (
<div>
<h4>{data.name}</h4>
<p>{membersFormatted}</p>
{isPrivate}
</div>
);
domainInfo = (
<div>
<h4>
<Button
btnAttrs={
{
className: 'text-success domain-name',
onClick: (e) => {
Utils.handleLink(e, `/domains/${domain.id}`);
}
}
}
>
{`@${domain.name}`}
</Button>
</h4>
</div>
);
btnsGeneralInfo = [
{
props: {
className: 'btn btn-xs btn-default',
onClick: (e) => {
Utils.handleLink(e, `/domains/${this.props.params.domain_id}/distribution_lists/${this.props.params.id}/edit`, this.props.location);
}
},
label: 'Editar'
}
];
actionTabsMembers = (
<ActionsPanel
name={'Miembros'}
data={arrayMembers}
onApplyChanges={(response) => {
this.onSubmitMembers(response);
}}
hasExport={true}
isEmail={true}
onDelete={(member) => {
this.onDeleteMember(member);
}}
onCancel={(response) => {
this.onCancelMember(response);
}}
/>
);
actionTabsAllows = (
<ActionsPanelAllows
name={'Permitidos'}
data={owners}
onApplyChanges={(response) => {
this.onSubmitOwners(response);
}}
hasExport={true}
isEmail={true}
onDelete={(owner) => {
this.onDeleteOwner(owner);
}}
onCancel={(response) => {
this.onCancelOwner(response);
}}
/>
);
}
if (this.state.error) {
if (Array.isArray(this.state.error)) {
message = this.state.error.map((err, i) => {
return (
<MessageBar
key={`new-error-${i}`}
message={err.error}
type={err.type}
autoclose={true}
/>
);
});
} else {
message = (
<MessageBar
message={this.state.error}
type={this.state.type}
autoclose={true}
/>
);
}
}
const pageInfo = (
<PageInfo
titlePage='lista de distribución'
descriptionPage='Para enviar correo a grupos de usuarios'
/>
);
const allows = (
<Panel
title='Permitidos'
hasHeader={false}
classHeader={'reset-panel'}
children={actionTabsAllows}
/>
);
const members = (
<Panel
title='Miembres'
hasHeader={false}
classHeader={'reset-panel'}
children={actionTabsMembers}
/>
);
panelTabs = (
<PanelTab
tabNames={['Miembros', 'Permitidos']}
tabs={{
miembros: members,
permitidos: allows
}}
location={this.props.location}
/>
);
if (this.state.notFound) {
return (
<div>
{pageInfo}
<div className='block-center text-center'>
<h3>{this.state.message}</h3>
</div>
</div>
);
}
return (
<div>
{pageInfo}
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-6 central-content'>
<Panel
title='Información General'
btnsHeader={btnsGeneralInfo}
children={generalData}
classHeader='with-min-height'
/>
</div>
<div className='col-md-6 central-content'>
<Panel
title='Dominio'
children={domainInfo}
classHeader='with-min-height'
/>
</div>
</div>
<div className='row'>
<div className='col-md-12 panel-with-tabs'>
{panelTabs}
</div>
</div>
</div>
</div>
);
}
}
DistributionLists.propTypes = {
location: React.PropTypes.object,
params: React.PropTypes.object
};
import $ from 'jquery';
import React from 'react';
import Button from '../button.jsx';
import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Utils from '../../utils/utils.jsx';
import EventStore from '../../stores/event_store.jsx';
import Promise from 'bluebird';
import Constants from '../../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class EditDistributionList extends React.Component {
constructor(props) {
super(props);
this.showMessage = this.showMessage.bind(this);
this.saveChanges = this.saveChanges.bind(this);
this.state = {};
}
showMessage(attrs) {
this.setState({
error: attrs.error,
type: attrs.typeError
});
}
saveChanges(e) {
e.preventDefault();
Utils.toggleStatusButtons('.action-edit-dl', true);
Utils.validateInputRequired(this.refs).then(() => {
const id = this.props.params.id;
const attrs = {};
const mail = this.refs.mail.value;
attrs.displayName = this.refs.displayName.value;
Client.modifyDistributionList(id, attrs, (dl) => {
const domain = dl.name.split('@').pop();
const newNameDL = `${mail}@${domain}`;
dl.rename(newNameDL, (err) => {
Utils.toggleStatusButtons('.action-edit-dl', false);
if (err) {
return GlobalActions.emitMessage({
error: err.extra.reason,
typeError: messageType.ERROR
});
}
return GlobalActions.emitMessage({
error: 'Se han actualizado sus datos éxitosamente.',
typeError: messageType.SUCCESS
});
});
}, (err) => {
GlobalActions.emitMessage({
error: err.message,
typeError: messageType.ERROR
});
Utils.toggleStatusButtons('.action-edit-dl', false);
});
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
type: messageType.ERROR
});
error.node.focus();
Utils.toggleStatusButtons('.action-edit-dl', false);
});
}
getDistributionList() {
const id = this.props.params.id;
new Promise((resolve, reject) => {
Client.getDistributionList(id, (success) => {
resolve(success);
}, (error) => {
reject(error);
});
}).then((res) => {
const domain = this.refs.domain;
const displayName = this.refs.displayName;
const mail = this.refs.mail;
const dl = res.name.split('@');
domain.innerHTML = dl.pop();
if (res.attrs.displayName) {
displayName.value = res.attrs.displayName;
}
mail.value = dl.shift();
}).catch((err) => {
GlobalActions.emitMessage({
error: err.message,
type: messageType.ERROR
});
Utils.toggleStatusButtons('.action-edit-dl', true);
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
componentDidMount() {
EventStore.addMessageListener(this.showMessage);
GlobalActions.emitEndLoading();
$('#sidebar-domain').removeClass('active');
this.getDistributionList();
}
componentWillUnmount() {
EventStore.removeMessageListener(this.showMessage);
$('#sidebar-domain').removeClass('active');
}
render() {
let message;
let form;
let actions;
if (this.state.error) {
message = (
<MessageBar
message={this.state.error}
type={this.state.type}
autoclose={true}
/>
);
}
form = (
<form
className='simple_form form-horizontal mailbox-form'
id='editAccount'
>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='requerido'>{'*'}</abbr>
{'Dirección'}
</label>
<div className='col-sm-8'>
<div className='input-group'>
<input
type='text'
className='form-control'
ref='mail'
data-required='true'
data-message='La dirección de correo es requerida, por favor verifique.'
placeholder='Dirección'
/>
<span
className='input-group-addon'
ref='domain'
>
</span>
</div>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Nombre'}
</label>
<div className='col-sm-8'>
<input
type='text'
className='form-control'
ref='displayName'
placeholder='Nombre mostrado en contactos'
/>
</div>
</div>
<div className='form-group'>
<div className='col-sm-3'></div>
<div className='col-sm-8'>
<Button
btnAttrs={
{
className: 'btn btn-info action-edit-dl',
onClick: (e) => {
this.saveChanges(e);
}
}
}
>
{'Guardar'}
</Button>
<Button
btnAttrs={
{
className: 'btn btn-default action-button',
onClick: (e) => {
Utils.handleLink(e, `/domains/${this.props.params.domain_id}/distribution_lists/${this.props.params.id}`);
}
}
}
>
{'Cancelar'}
</Button>
</div>
</div>
</form>
);
actions = [
{
label: 'Guardar',
props: {
className: 'btn btn-info btn-xs action-edit-dl',
onClick: (e) => {
this.saveChanges(e);
}
}
},
{
label: 'Cancelar',
props: {
className: 'btn btn-default btn-xs action-button',
onClick: (e) => {
Utils.handleLink(e, `/domains/${this.props.params.domain_id}/distribution_lists/${this.props.params.id}`);
}
}
}
];
return (
<div>
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
title={'Editar Lista de Distribución'}
btnsHeader={actions}
classHeader={'forum-box'}
>
{form}
</Panel>
</div>
</div>
</div>
</div>
);
}
}
EditDistributionList.propTypes = {
location: React.PropTypes.object,
params: React.PropTypes.any
};
......@@ -23,6 +23,7 @@ export default class DomainDistributionList extends React.Component {
}
getStateFromStores() {
const lists = DomainStore.getDistributionLists(this.props.domain);
return {
lists
};
......@@ -107,7 +108,7 @@ export default class DomainDistributionList extends React.Component {
<td className='distribution-list-actions'>
<a
href='#'
onClick={(e) => Utils.handleLink(e, `/distribution_lists/${dl.id}`)}
onClick={(e) => Utils.handleLink(e, `/domains/${domain.id}/distribution_lists/${dl.id}`)}
>
{'Ver'}
</a>
......
......@@ -36,7 +36,10 @@ export default class ConfirmDeleteModal extends React.Component {
message: 'Su contraseña se ha sido cambiada éxitosamente.',
typeError: 'text-success'
});
if (this.props.data.name === this.state.currentUser.name) {
this.forceLogout('/logout');
}
}, (error) => {
this.setState({
alert: true,
......
......@@ -5,6 +5,9 @@ import {browserHistory} from 'react-router';
import {Modal} from 'react-bootstrap';
import * as Utils from './../../utils/utils.jsx';
import * as Client from './../../utils/client.jsx';
import Promise from 'bluebird';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import MailboxStore from '../../stores/mailbox_store.jsx';
import React from 'react';
......@@ -30,31 +33,45 @@ export default class ConfirmDeleteModal extends React.Component {
}
handleDelete() {
new Promise((resolve, reject) => {
// start loading
GlobalActions.emitStartLoading();
Client.removeAccount(
this.props.data.id,
() => {
return resolve(true);
},
(error) => {
return reject(error);
}
);
}).then(() => {
MailboxStore.removeAccount(this.props.data);
this.setState(
{
alert: true,
message: `Su cuenta ${this.props.data.attrs.mail}, ha sido eliminada éxitosamente. Sera redirigido en ${this.state.timeToRedirect} seg`,
typeError: 'text-success'
typeError: 'text-warning'
}
);
Utils.toggleStatusButtons('.close-modal', true);
this.redirect();
},
() => {
}).catch((error) => {
this.setState(
{
alert: true,
message: `Hubo un error, al intentar eliminar su cuenta : ${this.props.data.attrs.mail}`,
typeError: 'text-danger'
}
);
message: error.message,
typeError: 'text-edanger'
}
);
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
redirect() {
const redirectAt = 1000;
setTimeout(() => {
if (this.timetoRedict-- < 1) {
browserHistory.replace('/mailboxes');
......@@ -65,7 +82,7 @@ export default class ConfirmDeleteModal extends React.Component {
this.redirect();
}
}, 1000);
}, redirectAt);
}
handleKeyUp() {
......@@ -120,7 +137,7 @@ export default class ConfirmDeleteModal extends React.Component {
<Modal.Footer>
<button
type='button'
className='btn btn-default'
className='btn btn-default close-modal'
onClick={this.props.onHide}
>
{'Cancelar'}
......
......@@ -3,10 +3,14 @@ import React from 'react';
import Panel from '../panel.jsx';
import Button from '../button.jsx';
import MessageBar from '../message_bar.jsx';
import Promise from 'bluebird';
import DataList from 'react-datalist';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import EventStore from '../../stores/event_store.jsx';
import MailboxStore from '../../stores/mailbox_store.jsx';
import Constants from '../../utils/constants.jsx';
......@@ -17,73 +21,168 @@ export default class CreateMailBox extends React.Component {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.getAllDomains = this.getAllDomains.bind(this);
this.showMessage = this.showMessage.bind(this);
this.handleRadioChanged = this.handleRadioChanged.bind(this);
this.controllerDataList = this.controllerDataList.bind(this);
this.reset = null;
this.state = {};
}
showMessage(attrs) {
this.setState({
error: attrs.message,
typeError: attrs.typeError
});
}
handleSubmit(e) {
e.preventDefault();
Utils.validateInputRequired(this.refs).then(() => {
// here assign missing properties.
let attrs = {
const domain = document.querySelector('input[list=\'domain\']');
if (domain.value === '') {
GlobalActions.emitMessage({
message: 'El dominio es requerido, verifique por favor.',
typeError: messageType.ERROR
});
domain.focus();
return false;
}
const email = this.refs.mail.value + '@' + domain.value;
//falta campo de tipo de casilla
const attrs = {
givenName: this.refs.givenName.value,
sn: this.refs.sn.value
sn: this.refs.sn.value,
description: this.refs.description.value,
zimbraCOSId: this.refs.zimbraCOSId.value,
zimbraAccountStatus: this.refs.zimbraAccountStatus.value
};
Client.createAccount(
this.refs.mail.value,
this.refs.mail.passwd,
email,
this.refs.passwd.value,
attrs,
(data) => {
// reset form when all is ok
document.getElementById('createAccount').reset();
this.setState(
{
error: `Su cuenta ${data.name} ha sido creada con èxito.`,
this.reset.toggleOptions();
MailboxStore.setMailbox(data);
GlobalActions.emitMessage({
message: 'Se ha creado su cuenta con éxito.',
typeError: messageType.SUCCESS
}
);
});
},
(error) => {
this.setState(
{
error: error.message,
typeError: messageType.WARNING
}
);
GlobalActions.emitMessage({
message: error.message,
typeError: messageType.ERROR
});
}
);
return true;
}).catch((err) => {
this.setState(
GlobalActions.emitMessage({
message: err.message,
typeError: messageType.ERROR
});
err.node.focus();
});
}
handleRadioChanged(val) {
this.refs.zimbraCOSId.value = val;
}
getAllDomains() {
const promises = [];
const max = 200;
const doms = new Promise((resolve, reject) => {
Client.getAllDomains(
{
error: err.message,
typeError: err.typeError
limit: max
},
(data) => {
resolve(data);
},
(error) => {
reject(error);
}
);
});
err.node.focus();
const cos = new Promise((resolve, reject) => {
Client.getAllCos((success) => {
resolve(success);
}, (error) => {
reject(error);
});
});
promises.push(doms, cos);
Promise.all(promises).then((success) => {
const response = {};
success.map((pos) => {
if (Array.isArray(pos)) {
const arrPlans = Utils.getEnabledPlansByCos(pos);
response.plans = arrPlans;
} else {
response.domains = pos;
}
return true;
});
componentDidMount() {
/*$('#selectDomains').select2({
placeholder: 'Por favor, introduzca 3 caracteres.',
allowClear: true
});*/
this.setState(response);
$('#passwdMeter').pwstrength();
GlobalActions.emitEndLoading();
Utils.toggleStatusButtons('.action-save', false);
}, (error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: error.type
});
GlobalActions.emitEndLoading();
Utils.toggleStatusButtons('.action-save', false);
});
}
componentDidMount() {
EventStore.addMessageListener(this.showMessage);
Utils.toggleStatusButtons('.action-save', true);
$('#sidebar-mailboxes').addClass('active');
this.getAllDomains();
GlobalActions.emitEndLoading();
}
componentWillUnmount() {
EventStore.removeMessageListener(this.showMessage);
$('#sidebar-mailboxes').removeClass('active');
}
controllerDataList(controller) {
this.reset = controller;
}
render() {
let message;
let domains = [];
let form = null;
let checkboxes = [];
if (this.state.error) {
message = (
<MessageBar
......@@ -94,7 +193,40 @@ export default class CreateMailBox extends React.Component {
);
}
let form = (
if (this.state.domains) {
const object = this.state.domains.domain;
const plans = this.state.plans;
let limit = object.length;
for (; limit-- > 0;) {
domains.push(object[limit].name);
}
for (let plan in plans) {
if (plans.hasOwnProperty(plan)) {
const item = (
<label
className='radio radio-info radio-inline pretty-input'
key={plan}
>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
onChange={() => {
this.handleRadioChanged(plans[plan]);
}}
/>
<span></span>
</div>
{Utils.titleCase(plan)}
</label>
);
checkboxes.push(item);
}
}
form = (
<form
className='simple_form form-horizontal mailbox-form'
onSubmit={(e) => {
......@@ -110,23 +242,24 @@ export default class CreateMailBox extends React.Component {
<div className='col-sm-8'>
<div className='row'>
<div className='col-xs-6'>
<div className='col-xs-12'>
<div className='input-group'>
<input
type='text'
className='form-control'
ref='mail'
data-required='true'
data-message='El nombre de la casilla es requerida, verifique por favor.'
placeholder='Mail'
/>
</div>
<div className='col-xs-6'>
<div className='input-group'>
<span className='input-group-addon'>{'@'}</span>
<select
<DataList
list='domain'
options={domains}
className='form-control'
id='selectDomains'
ref='domain'
>
</select>
placeholder='Dominio'
getController={this.controllerDataList}
/>
</div>
</div>
</div>
......@@ -176,21 +309,19 @@ export default class CreateMailBox extends React.Component {
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Administrador delegado'}
<label className='string col-sm-3 control-label'>
{'Status'}
</label>
<div className='col-sm-8'>
<label className='radio-inline pretty-input'>
<div className='pretty-checkbox'>
<input
type='checkbox'
className='pretty'
ref='zimbraIsDelegatedAdminAccount'
/>
<span></span>
</div>
</label>
<select
className='form-control'
ref='zimbraAccountStatus'
>
<option value='active'>Activa</option>
<option value='closed'>Cerrada</option>
<option value='locked'>Bloqueada</option>
</select>
</div>
</div>
......@@ -201,44 +332,15 @@ export default class CreateMailBox extends React.Component {
</label>
<div className='col-sm-8'>
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
ref='mailbox_basic'
/>
<span></span>
</div>
{'Básica'}
</label>
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
ref='mailbox_professional'
/>
<span></span>
</div>
{'Profesional'}
</label>
{checkboxes}
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
ref='mailbox_premium'
type='hidden'
ref='zimbraCOSId'
data-required='true'
data-message='El plan de su casilla es requerido, por favor verificar.'
/>
<span></span>
</div>
{'Premium'}
</label>
</div>
</div>
......@@ -253,6 +355,7 @@ export default class CreateMailBox extends React.Component {
type='password'
className='form-control'
data-required='true'
data-message='La contraseña de su casilla es requerida, verifique por favor.'
ref='passwd'
id='passwdMeter'
/>
......@@ -265,7 +368,7 @@ export default class CreateMailBox extends React.Component {
type='submit'
name='commit'
value='Guardar'
className='btn btn-primary'
className='btn btn-primary action-save'
/>
<Button
btnAttrs={
......@@ -283,6 +386,7 @@ export default class CreateMailBox extends React.Component {
</div>
</form>
);
}
const actions = [
{
......@@ -298,8 +402,8 @@ export default class CreateMailBox extends React.Component {
return (
<div>
{message}
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
......
......@@ -6,10 +6,15 @@ import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx';
import ConfirmDeleteModal from './confirm_delete_modal.jsx';
import ToggleModalButton from '../toggle_modal_button.jsx';
import DataList from 'react-datalist';
import UserStore from '../../stores/user_store.jsx';
import Promise from 'bluebird';
import MailboxStore from '../../stores/mailbox_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Utils from '../../utils/utils.jsx';
import EventStore from '../../stores/event_store.jsx';
import Constants from '../../utils/constants.jsx';
......@@ -23,12 +28,20 @@ export default class EditMailBox extends React.Component {
this.handleRadioChanged = this.handleRadioChanged.bind(this);
this.getMailbox = this.getMailbox.bind(this);
this.fillForm = this.fillForm.bind(this);
this.showMessage = this.showMessage.bind(this);
this.state = {};
}
showMessage(attrs) {
this.setState({
error: attrs.message,
typeError: attrs.typeError
});
}
handleRadioChanged(val) {
this.refs.plan.value = val;
this.refs.zimbraCOSId.value = val;
}
handleEdit(e) {
......@@ -36,41 +49,83 @@ export default class EditMailBox extends React.Component {
Utils.toggleStatusButtons('.action-button', true);
Utils.validateInputRequired(this.refs).then(() => {
let attrs = {
const domain = document.querySelector('input[list=\'domain\']');
if (domain.value === '') {
GlobalActions.emitMessage({
message: 'El dominio es requerido, verifique por favor.',
typeError: messageType.ERROR
});
domain.focus();
return false;
}
const email = this.refs.mail.value + '@' + domain.value;
// fill new attrs
const attrs = {
givenName: this.refs.givenName.value,
sn: this.refs.sn.value
sn: this.refs.sn.value,
description: this.refs.description.value,
zimbraCOSId: this.refs.zimbraCOSId.value,
zimbraAccountStatus: this.refs.zimbraAccountStatus.value
};
GlobalActions.emitStartLoading();
return new Promise((resolve, reject) => {
Client.modifyAccount(
this.state.data.id,
this.props.params.id,
attrs,
(data) => {
Utils.toggleStatusButtons('.action-button', false);
this.setState(
{
error: `Su cuenta ${data.name} ha sido modificada con èxito.`,
typeError: messageType.SUCCESS,
data: data
}
);
(account) => {
return resolve(account);
},
(error) => {
this.setState(
{
error: error.message,
typeError: messageType.WARNING
return reject(error);
}
);
Utils.toggleStatusButtons('.action-button', false);
}).then((account) => {
MailboxStore.changeAccount(account);
account.rename(email, (error, success) => {
if (error) {
this.setState({
data: account
});
return GlobalActions.emitMessage({
error: error.message,
typeError: messageType.ERROR
});
}
);
const newAccount = MailboxStore.changeAccount(success);
this.setState({
data: newAccount
});
return GlobalActions.emitMessage({
message: `Su cuenta ${account.name} ha sido modificada con èxito.`,
typeError: messageType.SUCCESS
});
});
}).catch((error) => {
GlobalActions.emitMessage({
message: error.message,
typeError: messageType.ERROR
});
}).finally(() => {
GlobalActions.emitEndLoading();
Utils.toggleStatusButtons('.action-button', false);
});
}).catch((err) => {
this.setState(
{
error: err.message,
typeError: err.typeError
}
);
GlobalActions.emitMessage({
message: err.message,
typeError: messageType.ERROR
});
err.node.focus();
Utils.toggleStatusButtons('.action-button', false);
......@@ -78,43 +133,151 @@ export default class EditMailBox extends React.Component {
}
getMailbox(id) {
Client.getAccount(
id,
(data) => {
const promises = [];
let data = null;
const max = 200;
Utils.toggleStatusButtons('.action-save', true);
if (MailboxStore.hasMailboxes()) {
data = MailboxStore.getMailboxById(id);
const domains = new Promise((resolve, reject) => {
Client.getAllDomains(
{
limit: max
},
(dataDomains) => {
return resolve(dataDomains);
},
(error) => {
return reject(error);
}
);
});
const cos = new Promise((resolve, reject) => {
Client.getAllCos((success) => {
resolve(success);
}, (error) => {
reject(error);
});
});
promises.push(domains, cos);
Promise.all(promises).then((result) => {
this.setState({
data
data,
domains: result.shift().domain,
cos: Utils.getEnabledPlansByCos(result.shift())
});
Utils.toggleStatusButtons('.action-save', false);
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: error.type
});
}).finally(() => {
GlobalActions.emitEndLoading();
});
} else {
const mailbox = new Promise((resolve, reject) => {
Client.getAccount(
id,
(resultMailbox) => {
return resolve(resultMailbox);
},
(error) => {
return reject(error);
}
);
});
const doms = new Promise((resolve, reject) => {
Client.getAllDomains(
{
limit: max
},
(domain) => {
return resolve(domain);
},
(error) => {
return reject(error);
}
);
});
const cos = new Promise((resolve, reject) => {
Client.getAllCos((success) => {
resolve(success);
}, (error) => {
reject(error);
});
});
promises.push(mailbox, doms, cos);
Promise.all(promises).then((result) => {
this.setState({
error: error.message
data: result.shift(),
domains: result.shift().domain,
cos: Utils.getEnabledPlansByCos(result.shift())
});
Utils.toggleStatusButtons('.action-save', false);
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: error.type
});
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
);
}
componentDidMount() {
/*Client.renameAccount('nuevomodificado@zboxapp.dev', (exito) => {
console.log('exito', exito);
}, (fallo) => {
console.log('fallo', fallo);
});*/
$('#sidebar-mailboxes').addClass('active');
GlobalActions.emitEndLoading();
EventStore.addMessageListener(this.showMessage);
this.getMailbox(this.props.params.id);
}
componentWillUnmount() {
EventStore.removeMessageListener(this.showMessage);
$('#sidebar-mailboxes').removeClass('active');
}
fillForm() {
let attrs = this.state.data.attrs;
this.refs.mail.value = this.state.data.name;
fillForm(data) {
const attrs = data.attrs;
this.refs.mail.value = data.name.split('@').shift();
this.refs.givenName.value = attrs.givenName || '';
this.refs.sn.value = attrs.sn;
this.refs.description.value = attrs.description || '';
this.refs.zimbraCOSId.value = attrs.zimbraCOSId || '';
this.refs.zimbraAccountStatus.value = attrs.zimbraAccountStatus;
}
render() {
let message;
let data;
let actions;
let form;
const domains = [];
let buttonDelete = null;
let currentDomain = '';
const cosElements = [];
let datalist = (
<input
type='text'
className='form-control'
placeholder='Dominio'
/>
);
if (this.state.error) {
message = (
<MessageBar
......@@ -125,13 +288,78 @@ export default class EditMailBox extends React.Component {
);
}
let data;
let actions;
let form;
if (this.state.data) {
data = this.state.data;
this.fillForm();
const doms = this.state.domains;
const cos = this.state.cos;
currentDomain = data.name.split('@').pop();
buttonDelete = (
<ToggleModalButton
role='button'
className='btn btn-xs btn-danger action-button'
dialogType={ConfirmDeleteModal}
dialogProps={{data}}
key='delete-mailbox'
>
{'Eliminar'}
</ToggleModalButton>
);
const length = doms.length;
for (let i = 0; i < length; i++) {
domains.push(doms[i].name);
}
for (let cosName in cos) {
if (cos.hasOwnProperty(cosName)) {
let isChecked = false;
const id = data.attrs.zimbraCOSId;
if (id) {
if (cos[cosName] === id) {
isChecked = 'checked';
}
}
const checkbox = (
<label
key={cos[cosName]}
className='radio radio-info radio-inline pretty-input'
>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
defaultChecked={isChecked}
onChange={() => {
this.handleRadioChanged(cos[cosName]);
}}
/>
<span></span>
</div>
{cosName}
</label>
);
cosElements.push(checkbox);
}
}
if (UserStore.getCurrentUser().name === data.name) {
buttonDelete = null;
}
this.fillForm(data);
datalist = (
<DataList
list='domain'
options={domains}
className='form-control'
placeholder='Dominio'
initialFilter={currentDomain}
/>
);
}
form = (
......@@ -150,24 +378,17 @@ export default class EditMailBox extends React.Component {
<div className='col-sm-8'>
<div className='row'>
<div className='col-xs-6'>
<div className='col-xs-12'>
<div className='input-group'>
<input
type='text'
className='form-control'
ref='mail'
data-required='true'
data-message='El campo direccion es requerido, verifique por favor.'
placeholder='Mail'
/>
</div>
<div className='col-xs-6'>
<div className='input-group'>
<span className='input-group-addon'>{'@'}</span>
<select
className='form-control'
id='selectDomains'
ref='domain'
>
</select>
{datalist}
</div>
</div>
</div>
......@@ -217,60 +438,34 @@ export default class EditMailBox extends React.Component {
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='Requerido'>{'*'}</abbr>
{'Tipo de casilla'}
<label className='string col-sm-3 control-label'>
{'Status'}
</label>
<div className='col-sm-8'>
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
onChange={() => {
this.handleRadioChanged('basica');
}}
/>
<span></span>
<select
className='form-control'
ref='zimbraAccountStatus'
>
<option value='active'>Activa</option>
<option value='closed'>Cerrada</option>
<option value='locked'>Bloqueada</option>
</select>
</div>
{'Básica'}
</label>
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
className='pretty'
name='mailbox'
type='radio'
onChange={() => {
this.handleRadioChanged('profesional');
}}
/>
<span></span>
</div>
{'Profesional'}
</label>
<label className='radio radio-info radio-inline pretty-input'>
<div className='pretty-radio'>
<input
type='radio'
className='pretty'
name='mailbox'
onChange={() => {
this.handleRadioChanged('premium');
}}
/>
<span></span>
</div>
{'Premium'}
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
<abbr title='Requerido'>{'*'}</abbr>
{'Tipo de casilla'}
</label>
<div className='col-sm-8'>
{cosElements}
<input
type='hidden'
ref='plan'
ref='zimbraCOSId'
data-required='true'
data-message='El plan de su cuenta es requerido, por favor verificar.'
/>
......@@ -314,24 +509,14 @@ export default class EditMailBox extends React.Component {
}
},
{
setComponent: (
<ToggleModalButton
role='button'
className='btn btn-xs btn-danger action-button'
dialogType={ConfirmDeleteModal}
dialogProps={{data}}
key='delete-mailbox'
>
{'Eliminar'}
</ToggleModalButton>
)
setComponent: buttonDelete
}
];
return (
<div>
{message}
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
......
import React from 'react';
import Datalist from 'react-datalist';
import Button from '../button.jsx';
import * as Utils from '../../utils/utils.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
const typeErrors = Constants.MessageType;
export default class FormAliasMailbox extends React.Component {
constructor(props) {
super(props);
this.handleDeleteAlias = this.handleDeleteAlias.bind(this);
this.sortAlias = this.sortAlias.bind(this);
this.addAlias = this.addAlias.bind(this);
this.toggleClass = 'glyphicon-sort-by-attributes';
this.asc = true;
this.state = {
alias: this.getAlias()
};
}
addAlias(e) {
e.preventDefault();
const refs = this.refs;
const newAlias = refs.alias.value + '@' + refs.domain.value;
Utils.validateInputRequired(refs).then(() => {
this.props.data.addAccountAlias(newAlias, () => {
GlobalActions.emitMessage({
message: 'Se ha agregado su nuevo Alias con éxito.',
type: typeErrors.SUCCESS
});
let alias = this.state.alias;
alias.push(newAlias);
this.setState({
alias: alias
});
}, (err) => {
GlobalActions.emitMessage({
message: err.error,
type: typeErrors.ERROR
});
});
}, (error) => {
GlobalActions.emitMessage({
message: error.message,
type: typeErrors.ERROR
});
error.node.focus();
});
}
sortAlias() {
let sort;
if (this.asc) {
sort = this.state.alias;
sort.sort().reverse();
this.setState({
alias: sort
});
this.asc = false;
this.toggleClass = 'glyphicon-sort-by-attributes';
} else {
sort = this.state.alias;
sort.sort();
this.setState({
alias: sort
});
this.asc = true;
this.toggleClass = 'glyphicon-sort-by-attributes-alt';
}
}
handleDeleteAlias(e, alias, index) {
e.preventDefault();
this.props.data.removeAccountAlias(alias, () => {
GlobalActions.emitMessage({
message: 'Se ha eliminado el alias éxitosamente.',
type: typeErrors.SUCCESS
});
const newAlias = Utils.removeIndexFromArray(this.state.alias, index);
this.setState({
alias: newAlias
});
}, (err) => {
GlobalActions.emitMessage({
message: err.error,
type: typeErrors.ERROR
});
});
}
getAlias() {
const data = this.props.data;
if (data.attrs.hasOwnProperty('zimbraMailAlias')) {
if (typeof data.attrs.zimbraMailAlias === 'string') {
return new Array(data.attrs.zimbraMailAlias);
}
return data.attrs.zimbraMailAlias.sort();
}
return [];
}
componentDidMount() {
let refs = this.refs;
refs.domain = refs.domain.refs.theInput;
refs.domain.setAttribute('data-required', 'true');
refs.domain.setAttribute('data-message', 'El dominio es necesario para crear el alias, verifiquelo por favor.');
}
render() {
let tbody;
let results;
// this bellow is just for testing porpuse
let options = ['apple', 'orange', 'pear', 'pineapple', 'melon'];
if (this.state.alias) {
tbody = this.state.alias.map((alias, i) => {
return (
<tr
key={`alias-${alias}-${i}`}
>
<td>
<span>{alias}</span>
<Button
btnAttrs={{
className: 'pull-right',
title: `Borrar el siguiente Alias : ${alias}`,
onClick: (e) => {
this.handleDeleteAlias(e, alias, i);
}
}}
>
<i className='fa fa-minus-circle text-danger'></i>
</Button>
</td>
</tr>
);
});
}
results = (
<table className='table table-striped table-bordered table-hover dataTable no-footer'>
<thead>
<tr>
<th>
{'Nombre'}
<span className='pull-right'>
<i
className={`glyphicon ${this.toggleClass} pull-right pointer`}
onClick={() => {
this.sortAlias();
}}
>
</i>
</span>
</th>
</tr>
</thead>
<tbody>
{tbody}
</tbody>
</table>
);
return (
<div className='row'>
<div className='col-xs-6'>
<div className='row'>
<form className='form-inline'>
<div className='col-xs-4'>
<div className='form-group'>
<label>
{'Mostrar'}
<select
ref='shown'
className='form-control input-sm'
>
<option value='10'>10</option>
<option value='25'>25</option>
<option value='50'>50</option>
<option value='100'>100</option>
</select>
</label>
</div>
</div>
<div className='col-xs-8 text-right'>
<label>
{'Buscar'}
<input
type='search'
className='form-control input-sm'
ref='search'
/>
</label>
</div>
</form>
</div>
<div className='row'>
<div className='col-xs-12'>
{results}
</div>
</div>
<div className='row'>
<div className='col-xs-6'>
<div className='dataTables_info'>
1 al 2 de 2 resultados
</div>
</div>
<div className='col-xs-6 text-right'>
<div className='btn-group'>
<Button
btnAttrs={
{
className: 'btn btn-default'
}
}
>
{'Anterior'}
</Button>
<Button
btnAttrs={
{
className: 'btn btn-default'
}
}
>
{'Siguiente'}
</Button>
</div>
</div>
</div>
</div>
<div className='col-xs-6'>
<div className='input-group'>
<input
type='text'
ref='alias'
className='form-control'
placeholder='Alias'
data-required='true'
data-message='El alias es requerido, compruebelo por favor'
/>
<span className='input-group-addon'>
@
</span>
<Datalist
list='domains'
options={options}
className='form-control'
id='domain'
ref='domain'
placeholder='Dominio'
/>
<span className='input-group-btn'>
<Button
btnAttrs={
{
className: 'btn btn-default',
onClick: (e) => {
this.addAlias(e);
}
}
}
>
Agregar alias
</Button>
</span>
</div>
</div>
</div>
);
}
}
FormAliasMailbox.propTypes = {
data: React.PropTypes.object
};
......@@ -30,12 +30,12 @@ export default class FormVacacionesMailbox extends React.Component {
Client.modifyAccount(data.id, attrs, () => {
GlobalActions.emitMessage({
message: 'Se ha modificado con éxito',
error: 'Se ha modificado su respuesta de vacaciones con éxito.',
type: messageType.SUCCESS
});
}, (error) => {
GlobalActions.emitMessage({
message: error.message,
error: error.message,
type: messageType.ERROR
});
});
......
import React from 'react';
import Button from '../button.jsx';
import StatusLabel from '../status_label.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 ZimbraStore from '../../stores/zimbra_store.jsx';
export default class BlockGeneralInfoMailbox extends React.Component {
constructor(props) {
......@@ -16,24 +19,22 @@ export default class BlockGeneralInfoMailbox extends React.Component {
this.state = {};
}
componentWillMount() {
this.domain = this.props.data.name.split('@');
this.domain = this.domain[this.domain.length - 1];
}
componentDidMount() {
this.getDomain();
}
getDomain() {
Client.getDomain(this.domain, (data) => {
const domain = Utils.getDomainFromString(this.data.name);
Client.getDomain(domain, (data) => {
this.setState({
hasDomain: true,
domainData: data
});
}, (error) => {
this.setState({
error: error.message
GlobalActions.emitMessage({
error: error.message,
typeError: error.type
});
});
}
......@@ -44,25 +45,64 @@ export default class BlockGeneralInfoMailbox extends React.Component {
render() {
let blockInfo = null;
let statusCos = null;
const cosID = Utils.getEnabledPlansObjectByCos(ZimbraStore.getAllCos(), this.props.data.attrs.zimbraCOSId);
let cosName = null;
switch (cosID.name) {
case 'premium':
cosName = Utils.titleCase(cosID.name);
statusCos = 'label btn-primary2 btn-xs';
break;
case 'professional':
cosName = Utils.titleCase(cosID.name);
statusCos = 'label btn-primary btn-xs';
break;
case 'basic':
cosName = Utils.titleCase(cosID.name);
statusCos = 'label btn-success btn-xs';
break;
default:
cosName = 'Sin Plan';
statusCos = 'label noplan btn-xs';
break;
}
if (this.state.hasDomain) {
const data = this.props.data;
const attrs = this.props.data.attrs;
const owner = (!attrs.givenName || !attrs.sn) ? null : attrs.givenName + ' ' + attrs.sn;
const mail = data.name;
//properties
const description = attrs.description;
const mailhost = attrs.zimbraMailHost;
const archive = attrs.zimbraArchiveAccount;
blockInfo = (
<article>
<article className='account-info'>
<div>
<h4>
<span className='mailbox-name text-success'>{this.data.name}</span>
<span className='mailbox-name text-success'>{mail}</span>
</h4>
{owner && (
<h5>
{owner}
</h5>
)}
</div>
{description && (
<div>
<p>
{this.data.attrs.description}
{description}
</p>
</div>
)}
<div>
<p>
<strong>{'Dominio'}: </strong>
<strong>{'Dominio: '}</strong>
<Button
btnAttrs={{
onClick: (e) => {
......@@ -70,10 +110,36 @@ export default class BlockGeneralInfoMailbox extends React.Component {
}
}}
>
{this.domain}
{this.state.domainData.name}
</Button>
</p>
</div>
{archive && (
<div>
<p>
<strong>{'Archive: '}</strong>
{archive}
</p>
</div>
)}
{mailhost && (
<div>
<p>
<strong>{'Mail Host'}: </strong>
{mailhost}
</p>
</div>
)}
<div>
<p>
<StatusLabel classes={statusCos}>
{cosName}
</StatusLabel>
</p>
</div>
</article>
);
}
......
......@@ -3,15 +3,17 @@
import $ from 'jquery';
import React from 'react';
import {browserHistory} from 'react-router';
import Promise from 'bluebird';
import EventStore from '../../stores/event_store.jsx';
import Button from '../button.jsx';
import MessageBar from '../message_bar.jsx';
import PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx';
import PanelTab from '../panel_tab.jsx';
import Pagination from '../pagination.jsx';
import MailboxStore from '../../stores/mailbox_store.jsx';
import Button from '../button.jsx';
import statusLabel from '../status_label.jsx';
import MessageBar from '../message_bar.jsx';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
......@@ -27,14 +29,7 @@ export default class Mailboxes extends React.Component {
constructor(props) {
super(props);
this.handleEdit = this.handleEdit.bind(this);
this.getMailboxes = this.getMailboxes.bind(this);
this.handleAddMailbox = this.handleAddMailbox.bind(this);
this.handleExportAsCSV = this.handleExportAsCSV.bind(this);
this.handleTabChanged = this.handleTabChanged.bind(this);
this.handleWatchInfo = this.handleWatchInfo.bind(this);
this.domainInfo = this.domainInfo.bind(this);
this.showMessage = this.showMessage.bind(this);
const page = parseInt(this.props.location.query.page, 10) || 1;
this.state = {
......@@ -43,6 +38,47 @@ export default class Mailboxes extends React.Component {
};
}
showMessage(attrs) {
this.setState({
error: attrs.error,
type: attrs.typeError
});
}
handleExportAsCSV(e) {
e.preventDefault();
const accounts = MailboxStore.getMailboxes();
if (accounts && accounts.account && accounts.account.length > 0) {
return Utils.exportAsCSV(accounts.account, 'all_accounts', true);
}
return false;
}
componentWillReceiveProps(newProps) {
const condition = this.props.location.query.page !== newProps.location.query.page;
if (condition) {
const page = parseInt(newProps.location.query.page, 10) || 1;
GlobalActions.emitStartLoading();
this.state = {
page,
offset: ((page - 1) * QueryOptions.DEFAULT_LIMIT)
};
this.getAllMailboxes();
} else {
GlobalActions.emitStartLoading();
let domainId;
if (newProps.params.domain_id !== this.props.params.domain_id) {
domainId = newProps.params.domain_id;
}
this.getAllMailboxes(domainId);
}
}
domainInfo(domainId) {
return new Promise(
(resolve, reject) => {
......@@ -65,131 +101,82 @@ export default class Mailboxes extends React.Component {
);
}
handleWatchInfo(e, path, location) {
Utils.handleLink(e, path, location);
getAccounts(domainName) {
const attrs = {};
if (domainName) {
attrs.domain = domainName;
}
getAccounts(domainName) {
Client.getAllAccounts(
{
limit: QueryOptions.DEFAULT_LIMIT,
offset: this.state.offset,
domain: domainName
},
(data) => {
this.setState({
data
});
GlobalActions.emitEndLoading();
},
(error) => {
this.setState({
error: error.message
new Promise((resolve, reject) => {
if (domainName) {
return Client.getAllAccounts(attrs, (success) => {
return resolve(success);
}, (error) => {
return reject(error);
});
GlobalActions.emitEndLoading();
}
);
}
getMailboxes() {
const domainId = this.props.params.domain_id;
if (domainId) {
return this.domainInfo(domainId).then(
() => {
const domain = DomainStore.getCurrent();
this.getAccounts(domain.name);
}
);
if (MailboxStore.hasMailboxes()) {
resolve(MailboxStore.getMailboxes());
}
return this.getAccounts();
return Client.getAllAccounts(attrs, (success) => {
MailboxStore.setMailboxes(success);
return resolve(success);
}, (error) => {
return reject(error);
});
}).then((data) => {
if (data.account) {
const tables = this.buildTableFromData(data, ['Todas', 'Bloqueadas']);
if (tables.lockedAlert) {
GlobalActions.emitMessage({
error: tables.lockedAlert.message,
typeError: messageType.LOCKED
});
}
handleAddMailbox(e, path) {
e.preventDefault();
if ((this.props.location.basename + this.props.location.pathname) !== path) {
browserHistory.push(path);
GlobalActions.emitStartLoading();
}
return this.setState({
data: tables
});
}
handleExportAsCSV(e) {
e.preventDefault();
return this.setState({
domainNotFound: domainName
});
}).catch(() => {
//console.log(error);
}).finally(() => {
GlobalActions.emitEndLoading();
});
}
handleTabChanged(tab) {
browserHistory.push(this.props.location.pathname + '?tab=' + tab);
getAllMailboxes(domainId) {
if (domainId) {
return this.domainInfo(domainId).then(() => {
const domain = DomainStore.getCurrent();
this.getAccounts(domain.name);
});
}
componentWillReceiveProps(newProps) {
if (this.props.location.query.page !== newProps.location.query.page) {
const page = parseInt(newProps.location.query.page, 10) || 1;
GlobalActions.emitStartLoading();
this.state = {
page,
offset: ((page - 1) * QueryOptions.DEFAULT_LIMIT)
};
this.getMailboxes();
}
return this.getAccounts();
}
componentDidMount() {
$('#sidebar-mailboxes').addClass('active');
this.getMailboxes();
EventStore.addMessageListener(this.showMessage);
const domainId = this.props.params.domain_id;
this.getAllMailboxes(domainId);
}
componentWillUnmount() {
EventStore.removeMessageListener(this.showMessage);
$('#sidebar-mailboxes').removeClass('active');
}
handleEdit(e, path) {
e.preventDefault();
if ((this.props.location.basename + this.props.location.pathname) !== path) {
GlobalActions.emitStartLoading();
browserHistory.push(path);
}
}
render() {
let message;
let panelTabs;
if (this.state.error) {
message = (
<MessageBar
message={this.state.error}
type={messageType.SUCCESS}
autoclose={true}
/>
);
}
let tableResults;
let total = 0;
const arrLocked = [];
if (this.state.data) {
const accounts = this.state.data.account || [];
total = accounts.length;
tableResults = accounts.map((mail) => {
let attrs = mail.attrs;
let statusClass = '';
let status = attrs.zimbraAccountStatus;
let id = mail.id;
switch (status) {
case 'locked':
statusClass = 'label label-warning';
status = 'Bloqueada';
arrLocked.push(mail);
break;
default:
statusClass = 'label label-success';
status = 'Activa';
break;
}
buildRow(row, classes, status) {
const id = row.id;
return (
<tr
key={id}
......@@ -197,25 +184,25 @@ export default class Mailboxes extends React.Component {
id={id}
>
<td className={'mailbox-name'}>
<statusLabel className={statusClass}>{status}</statusLabel>
<statusLabel className={classes}>{status}</statusLabel>
<Button
btnAttrs={{
btnAttrs={
{
className: 'mailbox-link',
onClick: (e) => {
this.handleWatchInfo(e, `mailboxes/${mail.id}`, location);
onClick: (e) => Utils.handleLink(e, 'mailboxes/' + id)
}
}
}}
>
{mail.name}
{row.name}
</Button>
</td>
<td className={'mailbox-displayname'}>
{mail.name}
{row.name}
</td>
<td className={'mailbox-cos-plan'}>
<statusLabel className={'label-plan label-unknown'}>unknown</statusLabel>
<statusLabel className={'label-plan label-unknown'}>{'unknown'}</statusLabel>
</td>
<td>
......@@ -223,9 +210,7 @@ export default class Mailboxes extends React.Component {
btnAttrs={
{
className: 'btn btn-xs btn-default',
onClick: (e) => {
this.handleEdit(e, '/mailboxes/' + mail.id + '/edit');
}
onClick: (e) => Utils.handleLink(e, '/mailboxes/' + id + '/edit')
}
}
>
......@@ -234,11 +219,25 @@ export default class Mailboxes extends React.Component {
</td>
</tr>
);
});
}
makeTable(rows, page) {
const hasPage = page || false;
let pagination = null;
if (hasPage) {
pagination = (
<Pagination
key='panelPagination'
url='mailboxes'
currentPage={this.state.page}
totalPages={hasPage.total}
/>
);
}
const panelBody = (
return (
<div
key='mailboxes-body'
key='mailbox'
id='index-mailboxes-table'
className='table-responsive'
>
......@@ -257,51 +256,67 @@ export default class Mailboxes extends React.Component {
</tr>
</thead>
<tbody>
{tableResults}
{rows}
</tbody>
</table>
{pagination}
</div>
);
}
const todas = `Todas (${total})`;
const archiving = 'Archiving';
const noPlan = `Sin Plan (${total})`;
const locked = `Bloqueada (${arrLocked.length})`;
const arrTabNames = [
todas,
archiving,
noPlan,
locked
];
insertToPanel(table, id, btns) {
const btn = btns || [];
let pagination;
if (this.state.offset > 0 || (this.state.data && this.state.data.more)) {
const totalPages = this.state.data ? Math.ceil(this.state.data.total / QueryOptions.DEFAULT_LIMIT) : 0;
const tab = this.props.location.query.tab || Utils.slug(todas);
pagination = (
<Pagination
key='panelPagination'
url={`mailboxes?tab=${tab}`}
currentPage={this.state.page}
totalPages={totalPages}
return (
<Panel
children={table}
key={id}
btnsHeader={btn}
/>
);
}
buildTableFromData(data, arrayTabNames) {
if (data.account) {
const accounts = data.account;
const totalAccounts = data.total;
const limit = data.account.length;
let activeAccounts = [];
let lockedAccounts = [];
const tabs = {};
for (let i = 0; i < limit; i++) {
const account = accounts[i].attrs;
switch (account.zimbraAccountStatus) {
case 'active':
activeAccounts.push(this.buildRow(accounts[i], 'label label-success m-r', 'Activa'));
break;
case 'closed':
activeAccounts.push(this.buildRow(accounts[i], 'label label-default m-r', 'Cerrada'));
break;
case 'locked':
activeAccounts.push(this.buildRow(accounts[i], 'label label-warning m-r', 'Inactiva'));
break;
case 'lockedout':
lockedAccounts.push(this.buildRow(accounts[i], 'label label-locked m-r', 'Bloqueada'));
activeAccounts.push(this.buildRow(accounts[i], 'label label-locked m-r', 'Bloqueada'));
break;
}
}
const response = {};
const all = `${arrayTabNames.shift()} (${activeAccounts.length})`;
const locked = `${arrayTabNames.shift()} (${lockedAccounts.length})`;
// create structure html for all accountsç
const icon = (
<div>
<i className='/fa fa-download'/>
<i className='fa fa-download'/>
<span>{'Exportar'}</span>
</div>
);
const tab1 = (
<div>
<Panel
title=''
children={[panelBody, pagination]}
btnsHeader={[
const btn = [
{
props: {
className: 'btn btn-default',
......@@ -314,82 +329,101 @@ export default class Mailboxes extends React.Component {
{
props: {
className: 'btn btn-success',
onClick: (e) => {
this.handleAddMailbox(e, '/mailboxes/new');
}
onClick: (e) => Utils.handleLink(e, '/mailboxes/new')
},
label: '+ Nueva Casilla'
}
]}
/>
</div>
);
];
const tab2 = (
<Panel
title='Casillas tab2'
children={[panelBody, pagination]}
/>
);
let activePagination = null;
const totalPage = Math.ceil(totalAccounts / QueryOptions.DEFAULT_LIMIT);
if (activeAccounts.length > QueryOptions.DEFAULT_LIMIT) {
activeAccounts = activeAccounts.slice(this.state.offset, (this.state.page * QueryOptions.DEFAULT_LIMIT));
activePagination = {
total: totalPage
};
}
const tab3 = (
<Panel
title='Casillas tb3'
children={panelBody}
let lockedPagination = null;
if (lockedAccounts.length > QueryOptions.DEFAULT_LIMIT) {
lockedAccounts = lockedAccounts.slice(this.state.offset, (this.state.page * QueryOptions.DEFAULT_LIMIT));
lockedPagination = {
total: totalPage
};
}
const tableActive = this.makeTable(activeAccounts, activePagination);
const panelActive = this.insertToPanel(tableActive, 'panel-all', btn);
// create structure html for all locked accounts
const tableLocked = this.makeTable(lockedAccounts, lockedPagination);
const panelLocked = this.insertToPanel(tableLocked, 'panel-locked');
arrayTabNames.push(all, locked);
tabs[Utils.slug(all)] = panelActive;
tabs[Utils.slug(locked)] = panelLocked;
response.tabNames = arrayTabNames;
response.tabs = tabs;
if (lockedAccounts.length > 0) {
const isPlural = (lockedAccounts.length > 1) ? true : null;
response.lockedAlert = {
total: lockedAccounts.length,
message: (isPlural) ? `${lockedAccounts.length} casillas bloqueadas` : `${lockedAccounts.length} casilla bloqueada`
};
}
return response;
}
return false;
}
render() {
let message = null;
if (this.state.error) {
message = (
<MessageBar
message={this.state.error}
type={this.state.type}
autoclose={false}
/>
);
}
const tab4 = (
<Panel
title='Casillas tab4'
children={panelBody}
let content = (
<div className='text-center'>
<h4>
No se han encontrado casillas para el dominio : {this.state.domainNotFound}
</h4>
</div>
);
const pagelInfo = (
<PageInfo
titlePage='Casillas'
descriptionPage='Usuarios de correo electrónico'
/>
);
const arrTabs = {};
arrTabs[Utils.slug(todas)] = tab1;
arrTabs[Utils.slug(archiving)] = tab2;
arrTabs[Utils.slug(noPlan)] = tab3;
arrTabs[Utils.slug(locked)] = tab4;
if (this.state.data) {
const data = this.state.data;
panelTabs = (
content = (
<PanelTab
tabNames={arrTabNames}
tabs={arrTabs}
tabNames={data.tabNames}
tabs={data.tabs}
location={this.props.location}
onClick={this.handleTabChanged}
/>
);
}
const content = panelTabs || '';
let title = 'Casillas';
const domain = DomainStore.getCurrent();
if (this.props.params.domain_id && domain) {
title = (
<div>
{'Casillas del dominio: '}
<a
href='#'
onClick={(e) => Utils.handleLink(e, `/domains/${domain.id}`)}
>
{`@${domain.name}`}
</a>
</div>
);
}
return (
<div>
<PageInfo
titlePage={title}
descriptionPage='Usuarios de correo electrónico'
/>
{message}
{pagelInfo}
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-12 panel-with-tabs'>
{content}
......
......@@ -12,14 +12,19 @@ import Panel from '../panel.jsx';
import BlockGeneralInfoMailbox from './info_general_mailbox.jsx';
import BlockStatsMailbox from './stats_mailbox.jsx';
import FormVacacionesMailbox from './form_resp_vacaciones_mailbox.jsx';
import FormAliasMailbox from './form_alias_mailbox.jsx';
import FormAliasMailbox from './../panel_actions.jsx';
import ChangePasswordModal from './change_passwd_modal.jsx';
import ToggleModalButton from '../toggle_modal_button.jsx';
import MessageBar from '../message_bar.jsx';
import Promise from 'bluebird';
import MailboxStore from '../../stores/mailbox_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 MailboxDetails extends React.Component {
constructor(props) {
......@@ -28,6 +33,8 @@ export default class MailboxDetails extends React.Component {
this.getMailbox = this.getMailbox.bind(this);
this.handleEdit = this.handleEdit.bind(this);
this.showMessage = this.showMessage.bind(this);
this.onRemoveAlias = this.onRemoveAlias.bind(this);
this.onCancelAlias = this.onCancelAlias.bind(this);
this.state = {};
}
......@@ -36,40 +43,97 @@ export default class MailboxDetails extends React.Component {
Utils.handleLink(e, path, location);
}
exportAsCSV(data) {
Utils.exportAsCSV(data, 'mailboxes_alias', true);
}
showMessage(attrs) {
this.setState({
error: attrs.message,
type: attrs.type.toLowerCase(),
autocloseInSecs: 10
error: attrs.error,
type: attrs.typeError
});
}
getMailbox() {
const id = this.props.params.id;
Utils.toggleStatusButtons('.action-info-btns', true);
onRemoveAlias(alias) {
MailboxStore.removeAlias(alias);
const items = MailboxStore.getAlias();
Client.getAccount(id, (data) => {
this.setState({
data
alias: items,
error: false
});
}
onCancelAlias(arrAlias) {
MailboxStore.refreshAlias(arrAlias);
const items = MailboxStore.getAlias();
this.setState({
alias: items,
error: false
});
}
getMailbox(id) {
if (MailboxStore.hasMailboxes()) {
const account = MailboxStore.getMailboxById(id);
MailboxStore.setCurrent(account);
let items = account.attrs.zimbraMailAlias;
if (items) {
if (!Array.isArray(items)) {
items = [items];
}
}
this.setState({
data: account,
alias: items
});
GlobalActions.emitEndLoading();
Utils.toggleStatusButtons('.action-info-btns', false);
} else {
return new Promise((resolve, reject) => {
Client.getAccount(id, (data) => {
return resolve(data);
}, (error) => {
this.setState({
notFound: true,
message: error.message
return reject(error);
});
}).then((result) => {
MailboxStore.setCurrent(result);
let items = result.attrs.zimbraMailAlias;
if (items) {
if (!Array.isArray(items)) {
items = [items];
}
}
this.setState({
data: result,
alias: items
});
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
type: error.typeError
});
}).finally(() => {
GlobalActions.emitEndLoading();
Utils.toggleStatusButtons('.action-info-btns', false);
});
}
return true;
}
componentDidMount() {
EventStore.addMessageListener(this.showMessage);
$('#sidebar-mailboxes').addClass('active');
this.getMailbox();
this.getMailbox(this.props.params.id);
}
componentWillUnmount() {
......@@ -77,6 +141,107 @@ export default class MailboxDetails extends React.Component {
$('#sidebar-mailboxes').removeClass('active');
}
onAliasSubmit(response) {
if (response.refresh) {
MailboxStore.refreshAlias(response.refresh);
}
this.multipleActions(response, this.state.data).then((result) => {
const limit = result.length;
const errors = [];
for (let index = 0; index < limit; index++) {
const res = result[index];
if (result[index].error) {
const action = (res.action === 'remove') ? 'eliminar' : 'agregar';
errors.push({
error: `Hubo un error al ${action} el alias: ${res.item}, debido a ${res.error.extra.reason}`,
type: MessageTypes.ERROR
});
}
if (res.isCompleted && res.action === 'add') {
MailboxStore.setAlias(res.item);
}
}
if (errors.length !== limit) {
errors.push({
error: 'Se han guardado los datos éxitosamente.',
type: MessageTypes.SUCCESS
});
}
this.setState({
error: errors,
alias: MailboxStore.getAlias()
});
response.reset();
});
}
multipleActions(response, account) {
const promises = [];
for (const key in response) {
if (response.hasOwnProperty(key) && key === 'add') {
const array = response[key];
const limit = array.length;
for (let index = 0; index < limit; index++) {
const res = {};
const promesa = new Promise((resolve) => {
account.addAccountAlias(array[index], (error) => {
if (error) {
res.isCompleted = false;
res.item = response[key][index];
res.action = key;
res.error = error;
} else {
res.isCompleted = true;
res.item = response[key][index];
res.action = key;
}
return resolve(res);
});
});
promises.push(promesa);
}
}
if (response.hasOwnProperty(key) && key === 'remove') {
const array = response[key];
const limit = array.length;
for (let index = 0; index < limit; index++) {
const res = {};
const promesa = new Promise((resolve) => {
account.removeAccountAlias(array[index], (error) => {
if (error) {
res.isCompleted = false;
res.item = response[key][index];
res.action = key;
res.error = error;
} else {
res.isCompleted = true;
res.item = response[key][index];
res.action = key;
}
return resolve(res);
});
});
promises.push(promesa);
}
}
}
return Promise.all(promises);
}
render() {
let generalData;
let statsData;
......@@ -86,6 +251,18 @@ export default class MailboxDetails extends React.Component {
let message;
if (this.state.error) {
if (Array.isArray(this.state.error)) {
message = this.state.error.map((err, i) => {
return (
<MessageBar
key={`new-error-${i}`}
message={err.error}
type={err.type}
autoclose={true}
/>
);
});
} else {
message = (
<MessageBar
message={this.state.error}
......@@ -94,6 +271,7 @@ export default class MailboxDetails extends React.Component {
/>
);
}
}
if (this.state.data) {
generalData = (
......@@ -151,7 +329,18 @@ export default class MailboxDetails extends React.Component {
const formAlias = (
<FormAliasMailbox
data={this.state.data}
name={'alias'}
data={this.state.alias}
isEmail={true}
onDelete={(alias) => {
this.onRemoveAlias(alias);
}}
onCancel={(arrAlias) => {
this.onCancelAlias(arrAlias);
}}
onApplyChanges={(response) => {
this.onAliasSubmit(response);
}}
/>
);
......@@ -204,8 +393,8 @@ export default class MailboxDetails extends React.Component {
return (
<div>
{pageInfo}
{message}
<div className='content animate-panel'>
{message}
<div className='row'>
<div className='col-md-6 central-content'>
<Panel
......
......@@ -7,7 +7,23 @@ export default class BlockGeneralInfoMailbox extends React.Component {
this.date = null;
this.status = null;
this.className = null;
this.lastConection = 'no se ha conectado';
this.lastConection = 'No se ha conectado';
this.getMailSize = this.getMailSize.bind(this);
this.state = {};
}
getMailSize() {
this.props.data.getMailboxSize((err, bytes) => {
let currentSize = '0 MB';
if (bytes) {
currentSize = Utils.bytesToMegas(bytes);
}
this.setState({
size: currentSize
});
});
}
componentWillMount() {
......@@ -16,24 +32,32 @@ export default class BlockGeneralInfoMailbox extends React.Component {
switch (this.props.data.attrs.zimbraAccountStatus) {
case 'inactive':
this.status = 'Desactivada';
this.className = 'btn btn-md btn-default';
this.className = 'btn-default mailbox-status';
break;
case 'locked':
this.status = 'Bloqueada';
this.className = 'btn btn-md btn-primary2';
this.className = 'btn-primary2 mailbox-status';
break;
default:
this.status = 'Activa';
this.className = 'btn btn-md btn-info';
this.className = 'btn-info mailbox-status';
break;
}
if (this.props.data.attrs.zimbraLastLogonTimestamp) {
this.lastConection = Utils.dateFormatted(this.props.data.attrs.zimbraLastLogonTimestamp);
}
this.getMailSize();
}
render() {
let size = null;
if (this.state.size) {
size = this.state.size;
}
return (
<div>
<div className='row'>
......@@ -59,7 +83,7 @@ export default class BlockGeneralInfoMailbox extends React.Component {
<div>
<p>
<span className='center-block'>Espacio Usado</span>
<strong>0 Bytes</strong>
<strong>{size}</strong>
</p>
</div>
</div>
......
......@@ -112,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', 'LOCKED']),
position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit']),
canClose: React.PropTypes.bool,
autoclose: React.PropTypes.bool,
......
......@@ -123,7 +123,7 @@ export default class Pagination extends React.Component {
return (
<div id='pagination'>
<ul className='pagination'>
<ul className='pagination pagination-sm'>
{first}
{prev}
{pages}
......
//import Datalist from 'react-datalist';
import React from 'react';
import Button from './button.jsx';
import PaginateArray from '../stores/paginate_array_store.jsx';
import DataList from 'react-datalist';
import * as Utils from '../utils/utils.jsx';
import * as GlobalActions from '../action_creators/global_actions.jsx';
import Constants from '../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class PanelActions extends React.Component {
constructor(props) {
super(props);
this.handleDelete = this.handleDelete.bind(this);
this.handleAddNew = this.handleAddNew.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleChangeLimit = this.handleChangeLimit.bind(this);
this.handleOnExport = this.handleOnExport.bind(this);
this.onSearch = this.onSearch.bind(this);
this.reset = this.reset.bind(this);
this.next = this.next.bind(this);
this.prev = this.prev.bind(this);
const limit = 10;
this.pagination = new PaginateArray(this.props.data, limit, 1);
this.add = [];
this.remove = [];
this.forAdd = [];
this.forRemove = [];
this.refresh = [];
const states = {};
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.state = states;
}
handleOnExport(e) {
e.preventDefault();
if (this.props.onExport) {
return this.props.onExport(this.state['items' + this.props.name]);
}
throw new Error('onExport function was not defined, onExport :' + this.props.onExport);
}
onSearch() {
const search = this.refs.search.value;
const arrayFiltered = this.props.data.filter((strArray) => {
if (strArray.match(search)) {
return strArray;
}
return false;
});
const states = {};
this.pagination.setArray(arrayFiltered);
this.pagination.reset();
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
prev() {
const prev = this.pagination.prevPage();
if (prev) {
const states = {};
states['items' + this.props.name] = prev;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
}
next() {
const next = this.pagination.nextPage();
if (next) {
const states = {};
states['items' + this.props.name] = next;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
}
handleDelete(e, item) {
e.preventDefault();
this.remove.push(item);
this.forRemove.push(item);
this.props.onDelete(item);
}
reset() {
this.add = [];
this.remove = [];
this.forAdd = [];
this.forRemove = [];
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
}
handleAddNew(e) {
e.preventDefault();
let item = null;
Utils.validateInputRequired(this.refs).then(() => {
if (this.props.hasComboInput) {
const domain = document.querySelector('input[list=\'domain\']');
if (domain.value === '') {
GlobalActions.emitMessage({
error: 'El dominio es requerido, verifique por favor.',
typeError: messageType.ERROR
});
domain.focus();
return false;
}
item = this.refs.addInput.value + '@' + domain.value;
if (this.props.isEmail) {
const emailPattern = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
const test = emailPattern.test(item);
if (!test) {
GlobalActions.emitMessage({
error: 'El correo no es correcto, verifiquelo por favor.',
typeError: messageType.ERROR
});
return false;
}
}
this.add.push(item);
this.forAdd.push(item);
} else {
item = this.refs.addInput.value;
if (this.props.isEmail) {
const emailPattern = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
const test = emailPattern.test(item);
if (!test) {
GlobalActions.emitMessage({
error: 'El correo no es correcto, verifíquelo por favor.',
typeError: messageType.ERROR
});
this.refs.addInput.focus();
return false;
}
}
this.refs.addInput.value = '';
this.add.push(item);
this.forAdd.push(item);
}
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
return true;
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: messageType.ERROR
});
error.node.focus();
});
}
componentWillReceiveProps(nextProps) {
this.pagination.setArray(nextProps.data);
this.pagination.reset();
const states = {};
states['items' + this.props.name] = nextProps.data;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
handleChangeLimit() {
const limit = this.refs.countItems.value;
this.pagination.setLimit(limit);
this.pagination.reset();
const states = {};
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
handleChange(e, item, action) {
if (action === 'remove') {
if (e.target.checked) {
this.forRemove.push(item);
this.getOutItemFromArray(this.refresh, item);
} else {
this.getOutItemFromArray(this.forRemove, item);
this.refresh.push(item);
}
return null;
}
if (e.target.checked) {
this.forAdd.push(item);
} else {
this.getOutItemFromArray(this.forAdd, item);
}
return null;
}
getOutItemFromArray(array, item) {
const length = array.length;
if (length > 0) {
for (let i = 0; i < length; i++) {
if (item === array[i]) {
array.splice(i, 1);
return true;
}
}
}
return false;
}
handleCancel() {
this.props.onCancel(this.remove);
this.remove = [];
this.add = [];
this.forRemove = [];
this.forAdd = [];
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
}
onSubmit() {
const response = {};
response.reset = this.reset;
this.refs.savebutton.setAttribute('disabled', 'disabled');
this.refs.savebutton.innerHTML = 'Aplicando Cambios...';
if (this.forAdd.length > 0) {
response.add = this.forAdd;
}
if (this.forRemove.length > 0) {
response.remove = this.forRemove;
}
if (this.refresh.length > 0) {
response.refresh = this.refresh;
}
this.props.onApplyChanges(response);
}
render() {
let rows = null;
let showName = `Agregar ${this.props.name}`;
let itemsForRemove = [];
let itemsForAdd = [];
let actionButtons = null;
let pagination = null;
let buttonExport = null;
let input = (
<div className='input-group'>
<input
type='text'
className='form-control'
placeholder={`${this.props.name}`}
ref='addInput'
data-required='true'
data-message={`${this.props.name} no pueda estar vacio, verifique por favor`}
/>
<span className='input-group-btn'>
<Button
btnAttrs={
{
className: 'btn btn-default pending_actions',
onClick: (e) => {
this.handleAddNew(e);
}
}
}
>
{showName}
</Button>
</span>
</div>
);
if (this.props.hasComboInput) {
input = (
<div className='input-group'>
<input
type='text'
ref='addInput'
className='form-control'
placeholder={this.props.name}
data-required='true'
data-message={`${this.props.name} no pueda estar vacio, verifique por favor`}
/>
<span className='input-group-addon'>
{'@'}
</span>
<DataList
list='dominio'
options={this.props.options}
placeHolder='Dominio'
/>
<span className='input-group-btn'>
<Button
btnAttrs={
{
className: 'btn btn-default pending_actions',
onClick: this.handleAddNew
}
}
>
{showName}
</Button>
</span>
</div>
);
}
if (this.state['items' + this.props.name] === 'undefined' || this.state['items' + this.props.name].length < 1) {
rows = (
<tr>
<td className='text-center'>
<strong>
{`No hay resultados para ${Utils.titleCase(this.props.name)}`}
</strong>
</td>
</tr>
);
} else {
const data = this.state['items' + this.props.name];
rows = data.map((row, index) => {
return (
<tr key={index}>
<td>
{row}
<Button
btnAttrs={
{
className: 'pull-right',
onClick: (e) => {
this.handleDelete(e, row);
}
}
}
>
<i className='fa fa-minus-circle text-danger'></i>
</Button>
</td>
</tr>
);
});
}
// make list adding / removing items
if (this.add.length > 0 || this.remove.length > 0) {
actionButtons = (
<div className='actions-buttons text-right'>
<button
className='btn btn-default pending_actions'
onClick={this.handleCancel}
>
{'Cancelar'}
</button>
<button
className='btn btn-primary pending_actions applyButtons'
onClick={() => {
this.onSubmit();
}}
ref='savebutton'
>
{'Guardar Cambios'}
</button>
</div>
);
if (this.add.length > 0) {
itemsForAdd = this.add.map((element, key) => {
return (
<label
className='list-inline listed-field'
key={`${this.props.name}labeladd${key}`}
>
<input
type='checkbox'
defaultChecked={'checked'}
data-value={element}
onChange={(e) => {
this.handleChange(e, element, 'add');
}}
/>
{element}
</label>
);
});
itemsForAdd = (
<div className='new-fields'>
<h5>Por Agregar</h5>
<div
className='new-fields-list'
>
{itemsForAdd}
</div>
</div>
);
}
if (this.remove.length > 0) {
itemsForRemove = this.remove.map((element, key) => {
return (
<label
className='list-inline listed-field'
key={`${this.props.name}labelremove${key}`}
>
<input
type='checkbox'
defaultChecked={'checked'}
data-value={element}
onChange={(e) => {
this.handleChange(e, element, 'remove');
}}
/>
{element}
</label>
);
});
itemsForRemove = (
<div className='new-fields'>
<h5>Por Eliminar</h5>
<div
className='new-fields-list'
>
{itemsForRemove}
</div>
</div>
);
}
}
if (this.state['pagination' + this.props.name]) {
pagination = this.state['pagination' + this.props.name];
}
if (this.props.hasExport && this.state['items' + this.props.name].length > 0) {
const icon = (
<div>
<i className='fa fa-download'/>
<span>{'Exportar'}</span>
</div>
);
buttonExport = (
<Button
btnAttrs={
{
className: 'btn btn-default',
onClick: (e) => {
this.handleOnExport(e);
}
}
}
>
{icon}
</Button>
);
}
return (
<div>
<div className='row'>
<div className='col-xs-6 clearfix'>
<div className='row'>
<form className='form-inline'>
<div className='col-xs-4'>
<div className='form-group'>
<label htmlFor='select-pages'>
{'Mostrar'}
<select
id='select-pages'
className='form-control input-sm margin-left'
ref='countItems'
onChange={this.handleChangeLimit}
>
<option value='10'>{'10'}</option>
<option value='25'>{'25'}</option>
<option value='50'>{'50'}</option>
<option value='100'>{'100'}</option>
</select>
</label>
</div>
</div>
<div className='col-xs-8 text-right'>
<label htmlFor='search'>
{'Buscar'}
<input
type='text'
className='form-control input-sm margin-left'
ref='search'
onKeyUp={() => {
this.onSearch();
}}
/>
</label>
</div>
</form>
</div>
<div className='col-xs-12'>
<div className='row'>
<table className='table table-striped table-bordered table-hover dataTable no-footer'>
<thead>
<tr>
<th>
{'Nombre'}
<span className='pull-right'>
<i
className='glyphicon pull-right pointer'
>
</i>
</span>
</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
<div className='col-xs-12 clearfix'>
<div className='row'>
<div className='dataTables_info pull-left'>
{buttonExport}
</div>
<div className='btn-group pull-right as-table'>
<span className='as-cell'>
{pagination}
</span>
<Button
btnAttrs={
{
className: 'btn btn-default pag-prev',
onClick: () => {
this.prev();
}
}
}
>
{'Anterior'}
</Button>
<Button
btnAttrs={
{
className: 'btn btn-default pag-next',
onClick: () => {
this.next();
}
}
}
>
{'Siguiente'}
</Button>
</div>
</div>
</div>
</div>
<div className='col-xs-6'>
<div className='col-xs-12'>
<div className='row'>
{input}
<div
className='wrap-controls'
ref='parent'
>
{itemsForAdd}
{itemsForRemove}
{actionButtons}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
PanelActions.propTypes = {
name: React.PropTypes.string.isRequired,
onApplyChanges: React.PropTypes.func.isRequired,
data: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.string
]),
options: React.PropTypes.array,
hasComboInput: React.PropTypes.bool,
onAdd: React.PropTypes.func,
onDelete: React.PropTypes.func,
onCancel: React.PropTypes.func,
hasExport: React.PropTypes.bool,
showNameOnButton: React.PropTypes.bool,
onExport: React.PropTypes.func,
isEmail: React.PropTypes.bool
};
PanelActions.defaultProps = {
hasComboInput: false,
hasExport: false,
showNameOnButton: true,
options: [],
isEmail: false
};
//import Datalist from 'react-datalist';
import React from 'react';
import Button from './button.jsx';
import PaginateArray from '../stores/paginate_array_store.jsx';
import DataList from 'react-datalist';
import * as Utils from '../utils/utils.jsx';
import * as GlobalActions from '../action_creators/global_actions.jsx';
import Constants from '../utils/constants.jsx';
const messageType = Constants.MessageType;
export default class PanelActions extends React.Component {
constructor(props) {
super(props);
this.handleDelete = this.handleDelete.bind(this);
this.handleAddNew = this.handleAddNew.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleChangeLimit = this.handleChangeLimit.bind(this);
this.handleOnExport = this.handleOnExport.bind(this);
this.onSearch = this.onSearch.bind(this);
this.reset = this.reset.bind(this);
this.next = this.next.bind(this);
this.prev = this.prev.bind(this);
const limit = 10;
this.pagination = new PaginateArray(this.props.data, limit, 1);
this.add = [];
this.remove = [];
this.forAdd = [];
this.forRemove = [];
this.refresh = [];
const states = {};
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.state = states;
}
handleOnExport(e) {
e.preventDefault();
if (this.props.onExport) {
return this.props.onExport(this.state['items' + this.props.name]);
}
throw new Error('onExport function was not defined, onExport :' + this.props.onExport);
}
onSearch() {
const search = this.refs.search.value;
const arrayFiltered = this.props.data.filter((strArray) => {
if (strArray.match(search)) {
return strArray;
}
return false;
});
const states = {};
this.pagination.setArray(arrayFiltered);
this.pagination.reset();
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
prev() {
const prev = this.pagination.prevPage();
if (prev) {
const states = {};
states['items' + this.props.name] = prev;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
}
next() {
const next = this.pagination.nextPage();
if (next) {
const states = {};
states['items' + this.props.name] = next;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
}
handleDelete(e, item) {
e.preventDefault();
this.remove.push(item);
this.forRemove.push(item);
this.props.onDelete(item);
}
reset() {
this.add = [];
this.remove = [];
this.forAdd = [];
this.forRemove = [];
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
}
handleAddNew(e) {
e.preventDefault();
let item = null;
Utils.validateInputRequired(this.refs).then(() => {
if (this.props.hasComboInput) {
const domain = document.querySelector('input[list=\'domain\']');
if (domain.value === '') {
GlobalActions.emitMessage({
error: 'El dominio es requerido, verifique por favor.',
typeError: messageType.ERROR
});
domain.focus();
return false;
}
item = this.refs.addInput.value + '@' + domain.value;
if (this.props.isEmail) {
const emailPattern = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
const test = emailPattern.test(item);
if (!test) {
GlobalActions.emitMessage({
error: 'El correo no es correcto, verifiquelo por favor.',
typeError: messageType.ERROR
});
return false;
}
}
this.add.push(item);
this.forAdd.push(item);
} else {
item = this.refs.addInput.value;
if (this.props.isEmail) {
const emailPattern = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
const test = emailPattern.test(item);
if (!test) {
GlobalActions.emitMessage({
error: 'El correo no es correcto, verifíquelo por favor.',
typeError: messageType.ERROR
});
this.refs.addInput.focus();
return false;
}
}
this.refs.addInput.value = '';
this.add.push(item);
this.forAdd.push(item);
}
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
return true;
}).catch((error) => {
GlobalActions.emitMessage({
error: error.message,
typeError: messageType.ERROR
});
error.node.focus();
});
}
componentWillReceiveProps(nextProps) {
this.pagination.setArray(nextProps.data);
this.pagination.reset();
const states = {};
states['items' + this.props.name] = nextProps.data;
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
handleChangeLimit() {
const limit = this.refs.countItems.value;
this.pagination.setLimit(limit);
this.pagination.reset();
const states = {};
states['items' + this.props.name] = this.pagination.init();
states['pagination' + this.props.name] = this.pagination.getResults();
this.setState(states);
}
handleChange(e, item, action) {
if (action === 'remove') {
if (e.target.checked) {
this.forRemove.push(item);
this.getOutItemFromArray(this.refresh, item);
} else {
this.getOutItemFromArray(this.forRemove, item);
this.refresh.push(item);
}
} else if (e.target.checked) {
this.forAdd.push(item);
} else {
this.getOutItemFromArray(this.forAdd, item);
}
}
getOutItemFromArray(array, item) {
const length = array.length;
if (length > 0) {
for (let i = 0; i < length; i++) {
if (item === array[i]) {
array.splice(i, 1);
return true;
}
}
}
return false;
}
handleCancel() {
this.props.onCancel(this.remove);
this.remove = [];
this.add = [];
this.forRemove = [];
this.forAdd = [];
const states = {};
states['change' + this.props.name] = true;
this.setState(states);
}
onSubmit() {
const response = {};
response.reset = this.reset;
this.refs.savebutton.setAttribute('disabled', 'disabled');
this.refs.savebutton.innerHTML = 'Aplicando Cambios...';
if (this.forAdd.length > 0) {
response.add = this.forAdd;
}
if (this.forRemove.length > 0) {
response.remove = this.forRemove;
}
if (this.refresh.length > 0) {
response.refresh = this.refresh;
}
this.props.onApplyChanges(response);
}
render() {
let rows = null;
let showName = `Agregar ${this.props.name}`;
let itemsForRemove = [];
let itemsForAdd = [];
let actionButtons = null;
let pagination = null;
let buttonExport = null;
let input = (
<div className='input-group'>
<input
type='text'
className='form-control'
placeholder={`${this.props.name}`}
ref='addInput'
data-required='true'
data-message={`${this.props.name} no pueda estar vacio, verifique por favor`}
/>
<span className='input-group-btn'>
<Button
btnAttrs={
{
className: 'btn btn-default pending_actions',
onClick: (e) => {
this.handleAddNew(e);
}
}
}
>
{showName}
</Button>
</span>
</div>
);
if (this.props.hasComboInput) {
input = (
<div className='input-group'>
<input
type='text'
ref='addInput'
className='form-control'
placeholder={this.props.name}
data-required='true'
data-message={`${this.props.name} no pueda estar vacio, verifique por favor`}
/>
<span className='input-group-addon'>
{'@'}
</span>
<DataList
list='dominio'
options={this.props.options}
placeHolder='Dominio'
/>
<span className='input-group-btn'>
<Button
btnAttrs={
{
className: 'btn btn-default pending_actions',
onClick: this.handleAddNew
}
}
>
{showName}
</Button>
</span>
</div>
);
}
if (this.state['items' + this.props.name] === 'undefined' || this.state['items' + this.props.name].length < 1) {
rows = (
<tr>
<td className='text-center'>
<strong>
{`No hay resultados para ${Utils.titleCase(this.props.name)}`}
</strong>
</td>
</tr>
);
} else {
const data = this.state['items' + this.props.name];
rows = data.map((row, index) => {
return (
<tr key={index}>
<td>
{row}
<Button
btnAttrs={
{
className: 'pull-right',
onClick: (e) => {
this.handleDelete(e, row);
}
}
}
>
<i className='fa fa-minus-circle text-danger'></i>
</Button>
</td>
</tr>
);
});
}
// make list adding / removing items
if (this.add.length > 0 || this.remove.length > 0) {
actionButtons = (
<div className='actions-buttons text-right'>
<button
className='btn btn-default pending_actions'
onClick={this.handleCancel}
>
{'Cancelar'}
</button>
<button
className='btn btn-primary pending_actions applyButtons'
onClick={() => {
this.onSubmit();
}}
ref='savebutton'
>
{'Guardar Cambios'}
</button>
</div>
);
if (this.add.length > 0) {
itemsForAdd = this.add.map((element, key) => {
return (
<label
className='list-inline listed-field'
key={`${this.props.name}labeladd${key}`}
>
<input
type='checkbox'
defaultChecked={'checked'}
data-value={element}
onChange={(e) => {
this.handleChange(e, element, 'add');
}}
/>
{element}
</label>
);
});
itemsForAdd = (
<div className='new-fields'>
<h5>Por Agregar</h5>
<div
className='new-fields-list'
>
{itemsForAdd}
</div>
</div>
);
}
if (this.remove.length > 0) {
itemsForRemove = this.remove.map((element, key) => {
return (
<label
className='list-inline listed-field'
key={`${this.props.name}labelremove${key}`}
>
<input
type='checkbox'
defaultChecked={'checked'}
data-value={element}
onChange={(e) => {
this.handleChange(e, element, 'remove');
}}
/>
{element}
</label>
);
});
itemsForRemove = (
<div className='new-fields'>
<h5>Por Eliminar</h5>
<div
className='new-fields-list'
>
{itemsForRemove}
</div>
</div>
);
}
}
if (this.state['pagination' + this.props.name]) {
pagination = this.state['pagination' + this.props.name];
}
if (this.props.hasExport && this.state['items' + this.props.name].length > 0) {
const icon = (
<div>
<i className='fa fa-download'/>
<span>{'Exportar'}</span>
</div>
);
buttonExport = (
<Button
btnAttrs={
{
className: 'btn btn-default',
onClick: (e) => {
this.handleOnExport(e);
}
}
}
>
{icon}
</Button>
);
}
return (
<div>
<div className='row'>
<div className='col-xs-6 clearfix'>
<div className='row'>
<form className='form-inline'>
<div className='col-xs-4'>
<div className='form-group'>
<label htmlFor='select-pages'>
{'Mostrar'}
<select
id='select-pages'
className='form-control input-sm margin-left'
ref='countItems'
onChange={this.handleChangeLimit}
>
<option value='10'>{'10'}</option>
<option value='25'>{'25'}</option>
<option value='50'>{'50'}</option>
<option value='100'>{'100'}</option>
</select>
</label>
</div>
</div>
<div className='col-xs-8 text-right'>
<label htmlFor='search'>
{'Buscar'}
<input
type='text'
className='form-control input-sm margin-left'
ref='search'
onKeyUp={() => {
this.onSearch();
}}
/>
</label>
</div>
</form>
</div>
<div className='col-xs-12'>
<div className='row'>
<table className='table table-striped table-bordered table-hover dataTable no-footer'>
<thead>
<tr>
<th>
{'Nombre'}
<span className='pull-right'>
<i
className='glyphicon pull-right pointer'
>
</i>
</span>
</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</div>
<div className='col-xs-12 clearfix'>
<div className='row'>
<div className='dataTables_info pull-left'>
{buttonExport}
</div>
<div className='btn-group pull-right as-table'>
<span className='as-cell'>
{pagination}
</span>
<Button
btnAttrs={
{
className: 'btn btn-default pag-prev',
onClick: () => {
this.prev();
}
}
}
>
{'Anterior'}
</Button>
<Button
btnAttrs={
{
className: 'btn btn-default pag-next',
onClick: () => {
this.next();
}
}
}
>
{'Siguiente'}
</Button>
</div>
</div>
</div>
</div>
<div className='col-xs-6'>
<div className='col-xs-12'>
<div className='row'>
{input}
<div
className='wrap-controls'
ref='parent'
>
{itemsForAdd}
{itemsForRemove}
{actionButtons}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
PanelActions.propTypes = {
name: React.PropTypes.string.isRequired,
onApplyChanges: React.PropTypes.func.isRequired,
data: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.string
]),
options: React.PropTypes.array,
hasComboInput: React.PropTypes.bool,
onAdd: React.PropTypes.func,
onDelete: React.PropTypes.func,
onCancel: React.PropTypes.func,
hasExport: React.PropTypes.bool,
showNameOnButton: React.PropTypes.bool,
onExport: React.PropTypes.func,
isEmail: React.PropTypes.bool
};
PanelActions.defaultProps = {
hasComboInput: false,
hasExport: false,
showNameOnButton: true,
options: [],
isEmail: false
};
{
"debug": true,
"debug": false,
"zimbraUrl": "http://zimbra.zboxapp.dev:8000/service/admin/soap",
"zimbraProxy": "https://zimbra.zboxapp.dev:7071",
"dnsApiUrl": "http://zimbra.zboxapp.dev:3000",
......
......@@ -20,6 +20,8 @@ import Mailboxes from './components/mailbox/mailbox.jsx';
import MailboxDetails from './components/mailbox/mailbox_details.jsx';
import CreateMailBox from './components/mailbox/create_mailbox.jsx';
import EditMailBox from './components/mailbox/edit_mailbox.jsx';
import DistributionLists from './components/distribution/distribution_lists.jsx';
import EditDistributionList from './components/distribution/edit_distribution_lists.jsx';
import * as Client from './utils/client.jsx';
import * as Utils from './utils/utils.jsx';
......@@ -77,12 +79,7 @@ function onPreLoggedIn(nextState, replace, callback) {
global.window.Zimbra = ZimbraStore.getCurrent();
}
const cos = ZimbraStore.getAllCos();
if (cos) {
return callback();
}
return Client.getAllCos(
Client.getAllCos(
(cosData) => {
ZimbraStore.setAllCos(cosData);
return callback();
......@@ -146,6 +143,14 @@ function renderRootComponent() {
path='domains/:domain_id/mailboxes'
component={Mailboxes}
/>
<Route
path='domains/:domain_id/distribution_lists/:id'
component={DistributionLists}
/>
<Route
path='domains/:domain_id/distribution_lists/:id/edit'
component={EditDistributionList}
/>
<Route
path='companies'
......
......@@ -18,4 +18,9 @@
background: $bg-warning-color;
color: $white;
}
.flash-locked {
background: $color-violet;
color: $white;
}
}
......@@ -9,6 +9,7 @@
@import 'lists';
@import 'loader';
@import 'modal';
@import 'panel_add';
@import 'panels';
@import 'progress_bar';
@import 'tabs';
......
.new-fields {
border-top: 1px solid $color-new-fields;
margin-top: 30px;
h5 {
margin: 5px 0;
}
.new-fields-list {
.listed-field {
margin: 8px 0 0 10px;
input {
margin-right: 5px;
}
}
}
}
.actions-buttons {
margin-top: 20px;
a {
margin-left: 5px;
&:first-child {
margin-left: 0;
}
}
}
.as-table {
display: table;
.as-cell {
display: table-cell;
padding-right: 10px;
vertical-align: middle;
}
}
.margin-left {
margin-left: 5px;
}
......@@ -614,7 +614,7 @@ a {
float: left;
height: 52px;
padding: 0;
width: 80%;
width: 70%;
.form-control {
background: none repeat scroll 0 0 $transparent;
......@@ -1272,7 +1272,7 @@ label {
.panel-body {
padding: 0;
.btn {
.btn:not(.pending_actions) {
margin: 0;
}
}
......
......@@ -54,3 +54,8 @@
}
}
}
.domain-name {
font-size: 22px;
font-weight: 600;
}
.noplan {
background: $black;
}
.account-info {
p {
margin-bottom: 3px;
}
}
.mailbox-status {
border: solid 1px;
border-radius: 2px;
color: $white;
display: inline-block;
padding: 5px;
text-align: center;
width: 100px;
}
.m-r {
margin-right: 5px;
}
.label-locked {
background: $color-violet;
}
......@@ -2,3 +2,4 @@
@import 'login';
@import 'domain';
@import 'companies';
@import 'mailbox';
......@@ -3,7 +3,6 @@
// Dependancies
@import '~bootstrap/dist/css/bootstrap.css';
@import '~jasny-bootstrap/dist/css/jasny-bootstrap.css';
@import '~perfect-scrollbar/dist/css/perfect-scrollbar.css';
@import '~font-awesome/css/font-awesome.css';
@import '~nprogress/nprogress.css';
@import '~toastr/build/toastr.css';
......
......@@ -84,3 +84,6 @@ $bg-border-color: #63a758;
$account-link-color: #337ab7;
//Panel add
$color-new-fields: #e4e5e7;
......@@ -10,6 +10,8 @@ class DomainStoreClass extends EventEmitter {
constructor() {
super();
this.current = null;
this.distributionListOwners = null;
this.distributionListMembers = null;
}
getCurrent() {
......@@ -129,6 +131,26 @@ class DomainStoreClass extends EventEmitter {
return lists;
}
getDistributionListById(listId, domain) {
if (this.current !== domain) {
this.setCurrent(domain);
}
const distributionLists = this.current.lists;
if (!distributionLists) {
return null;
}
for (const id in distributionLists) {
if (distributionLists.hasOwnProperty(id) && id === listId) {
return distributionLists[id];
}
}
return false;
}
setDistibutionLists(domain, listsArray) {
if (this.current !== domain) {
this.setCurrent(domain);
......@@ -151,6 +173,74 @@ class DomainStoreClass extends EventEmitter {
this.emitDistributionListsChange();
}
getMembers() {
if (this.distributionListMembers) {
return this.distributionListMembers;
}
return null;
}
setMembers(members) {
this.distributionListMembers = members;
}
addMember(member) {
if (this.distributionListMembers && Array.isArray(this.distributionListMembers)) {
this.distributionListMembers.push(member);
}
}
removeMember(member) {
if (this.distributionListMembers && Array.isArray(this.distributionListMembers)) {
const members = this.distributionListMembers;
const length = members.length;
for (let i = 0; i < length; i++) {
if (members[i] === member) {
members.splice(i, 1);
return true;
}
}
}
return false;
}
getOwners() {
if (this.distributionListOwners) {
return this.distributionListOwners;
}
return null;
}
setOwners(owners) {
this.distributionListOwners = owners;
}
addOwners(owner) {
if (this.distributionListOwners && Array.isArray(this.distributionListOwners)) {
this.distributionListOwners.push(owner);
}
}
removeOwner(owner) {
if (this.distributionListOwners && Array.isArray(this.distributionListOwners)) {
const owners = this.distributionListOwners;
const length = owners.length;
for (let i = 0; i < length; i++) {
if (owners[i] === owner) {
owners.splice(i, 1);
return true;
}
}
}
return false;
}
removeDistributionList(listId) {
if (this.current.lists) {
Reflect.deleteProperty(this.current.lists, listId);
......
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
import EventEmitter from 'events';
let mailboxesArray = null;
class MailboxStoreClass extends EventEmitter {
constructor() {
super();
this.current = null;
}
getMailboxById(id) {
if (mailboxesArray) {
const accounts = mailboxesArray.account;
const size = accounts.length;
for (let i = 0; i < size; i++) {
if (id === accounts[i].id) {
return accounts[i];
}
}
}
return false;
}
setMailbox(mailbox) {
if (mailboxesArray) {
const currentTotal = mailboxesArray.account.push(mailbox);
if (currentTotal > mailboxesArray.total) {
mailboxesArray.total = currentTotal;
}
}
}
setCurrent(account) {
this.current = account;
}
getCurrent() {
return this.current;
}
hasMailboxes() {
if (mailboxesArray) {
return true;
}
return false;
}
getMailboxes() {
return mailboxesArray;
}
setMailboxes(mailboxes) {
mailboxesArray = mailboxes;
}
changeAccount(newAccount) {
if (mailboxesArray) {
const accounts = mailboxesArray.account;
const size = accounts.length;
const id = newAccount.id;
for (let i = 0; i < size; i++) {
if (id === accounts[i].id) {
accounts[i] = newAccount;
return accounts[i];
}
}
}
return false;
}
removeAccount(account) {
if (mailboxesArray) {
const accounts = mailboxesArray.account;
const size = accounts.length;
const id = account.id;
for (let i = 0; i < size; i++) {
if (id === accounts[i].id) {
accounts.splice(i, 1);
if (mailboxesArray.total > 0) {
mailboxesArray.total = mailboxesArray.total - 1;
}
this.setMailboxes(mailboxesArray);
return true;
}
}
}
return false;
}
getAlias() {
let zimbraAlias;
if (this.current) {
if (this.current.attrs.zimbraMailAlias) {
if (!Array.isArray(this.current.attrs.zimbraMailAlias)) {
this.current.attrs.zimbraMailAlias = [this.current.attrs.zimbraMailAlias];
}
zimbraAlias = this.current.attrs.zimbraMailAlias;
} else {
this.current.attrs.zimbraMailAlias = [];
zimbraAlias = this.current.attrs.zimbraMailAlias;
}
return zimbraAlias;
}
return false;
}
setAlias(item) {
if (this.current) {
const alias = this.getAlias();
if (alias) {
alias.push(item);
}
}
}
removeAlias(alias) {
if (this.current) {
const aliasArray = this.getAlias();
if (aliasArray) {
const limit = aliasArray.length;
for (let i = 0; i < limit; i++) {
if (alias === aliasArray[i]) {
aliasArray.splice(i, 1);
return true;
}
}
}
}
return false;
}
refreshAlias(array) {
if (this.current) {
const alias = this.getAlias();
if (alias) {
Array.prototype.push.apply(alias, array);
}
return true;
}
return false;
}
}
const MailboxStore = new MailboxStoreClass();
export {MailboxStore as default};
// Copyright (c) 2016 ZBox, Spa. All Rights Reserved.
// See LICENSE.txt for license information.
class PaginateArrayStoreClass {
constructor(array, limit, page) {
this.getLength = this.getLength.bind(this);
this.paged = page || 1;
this.currentArray = array || [];
this.results = null;
this.limit = limit;
this.offset = ((this.paged - 1) * this.limit);
this.totalPage = Math.ceil(this.getLength() / this.limit);
}
init() {
return this.currentArray.slice(this.offset, (this.paged * this.limit));
}
nextPage() {
if ((this.paged + 1) > this.totalPage) {
return false;
}
this.paged += 1;
this.offset = ((this.paged - 1) * this.limit);
return this.currentArray.slice(this.offset, (this.paged * this.limit));
}
getLength() {
return this.currentArray.length;
}
prevPage() {
if ((this.paged - 1) < 1) {
return false;
}
this.paged -= 1;
this.offset = ((this.paged - 1) * this.limit);
return this.currentArray.slice(this.offset, (this.paged * this.limit));
}
setLimit(limit) {
this.limit = limit;
}
setArray(array) {
this.currentArray = array;
}
getResults() {
let pagination;
if (this.currentArray.length < 1) {
pagination = '0 resultados';
} else {
const start = (this.currentArray.length < 1) ? 0 : (this.offset + 1);
const end = ((this.paged * this.limit) > this.getLength()) ? this.getLength() : (this.paged * this.limit);
pagination = start + ' al ' + end + ' de ' + this.getLength();
}
return pagination;
}
reset() {
this.paged = 1;
this.offset = ((this.paged - 1) * this.limit);
this.resetTotalPage();
return this.init();
}
resetTotalPage() {
this.totalPage = Math.ceil(this.getLength() / this.limit);
}
}
//const PaginateArrayStore = new PaginateArrayStoreClass();
export {PaginateArrayStoreClass as default};
......@@ -556,3 +556,60 @@ export function getAllCos(success, error) {
}
);
}
export function getDistributionList(id, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.getDistributionList(id, (err, data) => {
if (err) {
const e = handleError('getDistributionList', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('getDistributionList', err);
return error(e);
}
);
}
export function modifyDistributionList(id, attrs, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.modifyDistributionList(id, attrs, (err, data) => {
if (err) {
const e = handleError('modifyDistributionList', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('modifyDistributionList', err);
return error(e);
}
);
}
export function renameAccount(account, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.renameAccount(account, (err, data) => {
if (err) {
const e = handleError('renameAccount', err);
return error(e);
}
return success(data);
});
},
(err) => {
const e = handleError('renameAccount', err);
return error(e);
}
);
}
......@@ -21,6 +21,7 @@ export default {
EventTypes: keyMirror({
DOMAIN_ADMINS_CHANGE_EVENT: null,
DOMAIN_DLS_CHANGE_EVENT: null,
ACCOUNT_CHANGE_EVENT: null,
START_LOADING_EVENT: null,
END_LOADING_EVENT: null,
USER_CHANGE_EVENT: null,
......@@ -32,7 +33,8 @@ export default {
SUCCESS: null,
WARNING: null,
ERROR: null,
INFO: null
INFO: null,
LOCKED: null
}),
ZimbraCodes: {
......
......@@ -311,3 +311,236 @@ export function getEnabledPlansByCosId(cosArray) {
return plans;
}
export function getMembers(members, label) {
let tag = label;
if (Array.isArray(members)) {
if (members.length === 1) {
tag = tag.slice(0, -1);
}
return members.length + ' ' + tag;
}
throw new Error('El tipo de members no es un arreglo : ' + typeof members);
}
export function bytesToMegas(bytes) {
let size = '0 MB';
if (bytes && typeof bytes === 'number') {
size = bytes;
const mb = 1024;
size = ((size / mb) / mb).toFixed(2) + 'MB';
}
return size;
}
export function getDomainFromString(string, otherwise) {
let domain = otherwise || 'Dominio Invalido';
if (typeof string === 'string' && string.lastIndexOf('@') > -1) {
domain = string.split('@');
domain = domain.pop();
}
return domain;
}
export function exportAsCSV(data, title, hasLabel) {
const info = (typeof data === 'object') ? data : JSON.parse(data);
let CSV = '';
CSV += title + '\r\n\n';
if (hasLabel) {
let row = '';
for (const index in info[0]) {
if (info[0].hasOwnProperty(index)) {
row += index + ',';
}
}
row = row.slice(0, row.length - 1);
CSV += row + '\r\n';
}
for (var i = 0; i < info.length; i++) {
let row = '';
for (var index in info[i]) {
if (info[i].hasOwnProperty(index)) {
row += '\'' + info[i][index] + '\',';
}
}
row = row.slice(0, -1);
CSV += row + '\r\n';
}
if (CSV === '') {
return;
}
const fileName = title.replace(/ /g, '_') + new Date().getTime();
const uri = 'data:text/csv;charset=utf-8,' + encodeURIComponent(CSV);
if (isSafari()) {
const time = 500;
var close = window.open('data:attachment/csv;charset=utf-8,' + encodeURIComponent(CSV), '_blank', 'width=1,height=1');
setTimeout(() => {
close.close();
}, time);
return;
}
const link = document.createElement('a');
link.href = uri;
link.style = 'visibility:hidden';
link.download = fileName + '.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
export function isChrome() {
if (navigator.userAgent.indexOf('Chrome') > -1) {
return true;
}
return false;
}
export function isSafari() {
if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
return true;
}
return false;
}
export function isIosChrome() {
// https://developer.chrome.com/multidevice/user-agent
return navigator.userAgent.indexOf('CriOS') !== -1;
}
export function isFirefox() {
return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
}
export function isIE() {
if (window.navigator && window.navigator.userAgent) {
var ua = window.navigator.userAgent;
return ua.indexOf('Trident/7.0') > 0 || ua.indexOf('Trident/6.0') > 0;
}
return false;
}
export function isEdge() {
return window.navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
}
export function addOrRemoveAlias(account, params) {
let promises = [];
for (const label in params) {
if (params.hasOwnProperty(label)) {
if (label === 'add') {
const length = params[label].length;
for (let i = 0; i < length; i++) {
let mypromise = new Promise((resolve) => {
account.addAccountAlias(params[label][i], (response) => {
if (response) {
resolve({
resolved: false,
err: response,
item: params[label][i],
action: 'add'
});
} else {
resolve({
resolved: true,
action: 'add'
});
}
});
});
promises.push(mypromise);
}
}
if (label === 'remove') {
const length = params[label].length;
for (let i = 0; i < length; i++) {
let mypromise = new Promise((resolve) => {
account.removeAccountAlias(params[label][i], (response) => {
if (response) {
resolve({
resolved: false,
err: response,
item: params[label][i],
action: 'remove'
});
} else {
resolve({
resolved: true,
action: 'remove'
});
}
});
});
promises.push(mypromise);
}
}
}
}
return Promise.all(promises);
}
export function getEnabledPlansObjectByCos(cosArray, cosID) {
const configPlans = global.window.manager_config.plans;
const plans = {};
const id = cosID || false;
cosArray.forEach((cos) => {
const key = cos.name;
if (configPlans.hasOwnProperty(key) && configPlans[key]) {
if (id) {
if (cos.id === id) {
plans.name = key;
plans.id = cos.id;
plans.attrs = cos.attrs;
}
} else {
plans[key] = {};
plans[key].id = cos.id;
plans[key].attrs = cos.attrs;
}
}
});
return plans;
}
export function getOwners(owners) {
if (Array.isArray(owners)) {
const limit = owners.length;
const ownersArray = [];
for (let i = 0; i < limit; i++) {
ownersArray.push(owners[i].name);
}
return ownersArray;
}
throw Error('Owners array no es un arreglo :' + typeof owners);
}
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