Commit 7fead81c authored by Juorder Gonzalez's avatar Juorder Gonzalez Committed by GitHub

Merge pull request #239 from ZBoxApp/salesApp

add app sales integrated with manager zbox app, only for global admin…
parents e315728f 4cee35f0
1466617142
1467382402
\ No newline at end of file
......@@ -58,7 +58,7 @@ stop-server:
kill $$PROCID; \
done
@for PROCID in $$(ps -ef | grep "[b]abel-node.*companies" | awk '{ print $$2 }'); do \
@for PROCID in $$(ps -ef | grep "[b]abel-node.*sales" | awk '{ print $$2 }'); do \
echo stopping server $$PROCID; \
kill $$PROCID; \
done
......@@ -66,4 +66,4 @@ stop-server:
start-server:
@echo Starting ZBox Manager 2.0 Test Server
@npm run server &
@npm run companies-service &
@npm run sales-service &
......@@ -75,6 +75,7 @@
"run": "webpack --progress --watch",
"run-fullmap": "webpack --progress --watch",
"companies-service": "babel-node companies-service.js",
"server": "babel-node server.js"
"server": "babel-node server.js",
"sales-service": "babel-node sales-services.js"
}
}
module.exports = {"main":{"js":"/419997bundle.js"}}
\ No newline at end of file
module.exports = {"main":{"js":"/312387bundle.js"}}
\ No newline at end of file
/**
* Created by enahum on 4/25/16.
*/
const http = require('http');
const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const Promise = require('bluebird');
const _ = require('lodash');
const moment = require('moment');
const port = normalizePort(process.env.PORT || '3300'); //eslint-disable-line no-process-env
const app = express();
app.set('port', port);
app.use(cors());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.get('/list', (req, res) => {
readSales().
then((data) => {
return res.json(data);
}).
catch((err) => {
return res.status(500).json(err);
});
});
app.post('/prices/mailboxes', (req, res) => {
const data = req.body;
const domainId = data.domainId;
readSales().then((data) => {
const prices = _.find(data, {domainId: domainId});
if (prices) {
return res.json(prices);
}
return res.status(404).json({
code: 404,
message: 'DomaindId ' + domainId + ' not found'
});
}).catch((err) => {
return res.status(500).json(err);
});
});
/*app.get('/sales/prices', (req, res) => {
return res.json([
{
number: 355,
link: 'http://google.com',
date: moment('2016-01-01').toJSON(),
total: '235581',
status: 1
},
{
number: 356,
link: 'http://google.com',
date: moment('2016-02-01').toJSON(),
total: '27581',
status: 2
},
{
number: 357,
date: moment('2016-02-01').toJSON(),
total: '30581',
status: 3
},
{
number: 358,
link: 'http://google.com',
date: moment('2016-03-01').toJSON(),
total: '35581',
status: 0
}
]);
});*/
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use((err, req, res) => {
res.status(err.status || 500);
res.json({
message: err.message,
error: err
});
});
}
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
const p = parseInt(val, 10);
if (isNaN(p)) {
// named pipe
return val;
}
if (p >= 0) {
// port number
return p;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges'); //eslint-disable-line no-console
process.exit(1); //eslint-disable-line no-process-exit
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use'); //eslint-disable-line no-console
process.exit(1); //eslint-disable-line no-process-exit
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
const addr = server.address();
const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
console.info('ZBox Sales Test Server listening on ' + bind); //eslint-disable-line no-console
}
function readSales() {
return new Promise((resolve, reject) => {
const filename = path.join(__dirname, 'sales', 'sales.json');
fs.readFile(filename, (err, data) => {
if (err) {
return reject(err);
}
return resolve(JSON.parse(data));
});
});
}
[
{
"domainId": "fda37354-fbec-47f5-bb26-383cf2ffe7a2",
"currency": "CLP",
"prices": {
"basic": 1.490,
"professional": 1.990,
"premium": 3.490
}
},
{
"domainId": "3d450d43-54b9-401d-bc48-a77f80baf152",
"currency": "CLP",
"prices": {
"basic": 1.490,
"professional": 1.990,
"premium": 3.490
}
},
{
"domainId": null,
"currency": "CLP",
"prices": {
"basic": 20.000,
"professional": 23.000,
"premium": 40.000
}
},
{
"domainId": null,
"currency": "CLP",
"prices": {
"basic": 20.000,
"professional": 23.000,
"premium": 40.000
}
},
{
"domainId": null,
"currency": "CLP",
"prices": {
"basic": 20.000,
"professional": 23.000,
"premium": 40.000
}
}
]
......@@ -7,6 +7,7 @@ import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as Utils from '../../utils/utils.jsx';
......@@ -47,6 +48,7 @@ export default class DomainMailboxPlans extends React.Component {
}
const headerButtons = [
{
label: 'Ver casillas',
props: {
......@@ -63,6 +65,18 @@ export default class DomainMailboxPlans extends React.Component {
}
];
if (UserStore.isGlobalAdmin()) {
headerButtons.unshift(
{
label: 'Comprar Casillas',
props: {
className: 'btn btn-info btn-xs',
onClick: (e) => Utils.handleLink(e, `/sales/${this.props.params.id}/mailboxes`, this.props.location)
}
}
);
}
const mailboxPlans = [];
let panelBody = null;
const cos = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
......
......@@ -14,6 +14,7 @@ import EventStore from '../../stores/event_store.jsx';
import MailboxStore from '../../stores/mailbox_store.jsx';
import DomainStore from '../../stores/domain_store.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
import UserStore from '../../stores/user_store.jsx';
import Constants from '../../utils/constants.jsx';
......@@ -267,12 +268,55 @@ export default class CreateMailBox extends React.Component {
});
}
handleRadioChanged(val) {
this.setState({
handleRadioChanged(e, val) {
const {enabledAccounts} = this.state;
let needToBuy = false;
let data = {};
if (enabledAccounts) {
const keys = Object.keys(enabledAccounts);
keys.forEach((pos) => {
if (enabledAccounts[pos].cosId === val && enabledAccounts[pos].enabled < 1) {
needToBuy = true;
data = enabledAccounts[pos];
}
});
}
if (!needToBuy) {
return this.setState({
zimbraCOSId: val
});
}
e.target.checked = false;
const {zimbraCOSId} = this.state;
if (zimbraCOSId.length > 0) {
this.setState({
zimbraCOSId: ''
});
}
if (UserStore.isGlobalAdmin()) {
const options = {
title: 'Comprar Casilla',
text: `Por ahora no tienes más cupo para crear una casilla tipo <strong>${Utils.titleCase(data.plan)}</strong>, ¿Deseas comprar más?`,
html: true,
confirmButtonText: 'Si, compraré'
};
return Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
const {id} = this.cacheDomain;
if (id) {
return Utils.handleLink(null, `/sales/${id}/mailboxes`);
}
}
}, options);
}
}
getAllDomains() {
const max = 200;
......@@ -422,7 +466,7 @@ export default class CreateMailBox extends React.Component {
keyPlans.forEach((plan) => {
if (plans[plan]) {
let isDisabled = null;
//let isDisabled = null;
let classCss = null;
let info = null;
let hasPlan = false;
......@@ -430,7 +474,7 @@ export default class CreateMailBox extends React.Component {
this.state.enabledAccounts.forEach((p) => {
if (plans[plan] === p.cosId) {
hasPlan = true;
isDisabled = p.enabled < 1 ? true : null;
//isDisabled = p.enabled < 1 ? true : null;
classCss = p.classCss;
info = (
<div>
......@@ -448,7 +492,7 @@ export default class CreateMailBox extends React.Component {
}
if (this.state.enabledAccounts && !hasPlan && null) {
isDisabled = true;
//isDisabled = true;
info = (
<div>
<span className='text-danger'>
......@@ -458,11 +502,11 @@ export default class CreateMailBox extends React.Component {
);
}
const disabledCss = isDisabled ? 'disabled' : '';
//const disabledCss = isDisabled ? 'disabled' : '';
const item = (
<label
className={`radio radio-info radio-inline pretty-input ${disabledCss}`}
className={'radio radio-info radio-inline pretty-input'}
key={plan}
>
<div className='pretty-radio'>
......@@ -470,10 +514,9 @@ export default class CreateMailBox extends React.Component {
type='radio'
className='pretty'
name='mailbox'
onChange={() => {
this.handleRadioChanged(plans[plan]);
onChange={(e) => {
this.handleRadioChanged(e, plans[plan]);
}}
disabled={isDisabled}
/>
<span></span>
</div>
......
......@@ -9,6 +9,7 @@ import MessageBar from '../message_bar.jsx';
import Panel from '../panel.jsx';
import DataList from 'react-datalist';
import Promise from 'bluebird';
import currencyFormatter from 'currency-formatter';
import * as Client from '../../utils/client.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
......@@ -39,7 +40,11 @@ export default class EditMailBox extends React.Component {
this.cos = Utils.getEnabledPlansByCos(ZimbraStore.getAllCos());
this.editUrlFromParams = this.props.params.domain_id ? `/domains/${this.props.params.domain_id}/mailboxes/` : '/mailboxes/';
this.currency = window.manager_config.invoiceAPI.currency;
const precision = this.currency === 'CLP' ? 0 : 2;
this.currencyParams = {code: this.currency, symbol: '', precision};
this.domainId = null;
this.currentPlan = '';
this.state = {
zimbraCOSId: ''
......@@ -53,12 +58,155 @@ export default class EditMailBox extends React.Component {
});
}
handleRadioChanged(val) {
this.setState({
handleRadioChanged(e, val) {
const {enabledAccounts} = this.state;
let needToUpgrade = false;
let data = null;
const currentEvent = Object.assign({}, e);
if (enabledAccounts) {
if (val && val.trim().length > 0) {
enabledAccounts.forEach((plan) => {
if (plan.cosId === val && plan.enabled < 1) {
needToUpgrade = true;
data = plan;
}
});
}
}
if (!needToUpgrade) {
return this.setState({
zimbraCOSId: val
});
}
currentEvent.target.checked = false;
const {zimbraCOSId} = this.state;
if (zimbraCOSId.length > 0) {
this.setState({
zimbraCOSId: ''
});
}
if (UserStore.isGlobalAdmin()) {
const options = {
title: 'Plan sin cupo',
text: `Actualmente el plan <strong>${Utils.titleCase(data.plan)}</strong> no tiene cupo para completar la acción solicitada, por lo cual es necesario que compre más casillas`,
html: true,
confirmButtonText: 'Obtener Precio',
showLoaderOnConfirm: true,
closeOnConfirm: false
};
const domainId = this.domainId || this.props.params.domain_id || null;
const dataJSON = {
domainId,
type: 'standar',
currency: this.currency,
cosId: val,
anualRenovation: true,
upgrade: true
};
const request = {
domainId: this.domainId,
adminEmail: UserStore.getCurrentUser().name,
upgrade: true,
currency: this.currency
};
const account = this.state.data;
Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
Client.getPrices(dataJSON, (success) => {
const prices = success.result.prices;
const price = prices[data.plan] ? currencyFormatter.format(prices[data.plan], this.currencyParams) + ' ' + this.currency : 0;
const options = {
title: 'Cambio de Plan',
text: `Al presionar <strong>Aceptar</strong>, está autorizando la emisión de una factura por un total de <strong>${price}</strong> correspondiente a : <br> <ul class="text-left"><li>Asunto: Cambio de Plan</li><li>Casilla: <strong>${account.name}</strong></li><li>Plan Original: <strong>${this.currentPlan}</strong></li><li>Nuevo Plan: <strong>${data.plan}</strong></li></ul>`,
html: true,
confirmButtonText: 'Si, Cambiar Plan',
showLoaderOnConfirm: true,
closeOnConfirm: false
};
Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
const item = {};
item.basic = {
type: 'Producto',
quantity: 1,
price: prices[data.plan],
description: `Cambio de plan de la casilla: ${account.name} de ${this.currentPlan} a ${data.plan}`,
id: data.cosId
};
request.items = item;
const requestObject = JSON.stringify(request);
Client.makeSale(requestObject, () => {
Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
const enabledAccounts = this.state.enabledAccounts;
enabledAccounts.forEach((plan) => {
if (plan.cosId === data.cosId) {
plan.enabled++;
plan.total++;
currentEvent.target.checked = true;
}
});
this.setState({
enabledAccounts
});
}
}, {
title: 'Cambio de Plan',
text: 'Su compra se ha realizado con éxito.',
showCancelButton: false,
confirmButtonColor: '#4EA5EC',
confirmButtonText: 'Muy bien',
type: 'success'
});
}, (error) => {
Utils.alertToBuy(() => {
return null;
}, {
title: 'Error',
text: error.message || error.error.message || 'Ha ocurrido un error desconocido.',
showCancelButton: false,
confirmButtonColor: '#4EA5EC',
confirmButtonText: 'Entiendo',
type: 'error',
closeOnConfirm: true
});
});
}
}, options);
}, (error) => {
return EventStore.emitToast({
type: 'error',
title: 'Compras - Precios',
body: error.message || 'Ha ocurrido un error al intentar obtener los precios, vuelva a intentarlo por favor.',
options: {
timeOut: 4000,
extendedTimeOut: 2000,
closeButton: true
}
});
});
}
}, options);
}
}
removeAccount() {
const account = this.state.data;
const response = {
......@@ -652,12 +800,13 @@ export default class EditMailBox extends React.Component {
keyPlans.forEach((plan) => {
if (cos[plan]) {
let isChecked = false;
let isDisabled = null;
//let isDisabled = null;
let classCss = null;
let info = null;
let hasPlan = false;
if (id) {
if (cos[plan] === id) {
this.currentPlan = plan;
isChecked = 'checked';
}
}
......@@ -665,7 +814,7 @@ export default class EditMailBox extends React.Component {
this.state.enabledAccounts.forEach((p) => {
if (cos[plan] === p.cosId) {
hasPlan = true;
isDisabled = p.enabled < 1 ? true : null;
//isDisabled = p.enabled < 1 ? true : null;
classCss = p.classCss;
info = (
<div>
......@@ -683,7 +832,7 @@ export default class EditMailBox extends React.Component {
}
if (this.state.enabledAccounts && !hasPlan && null) {
isDisabled = true;
//isDisabled = true;
info = (
<div>
<span className='text-danger'>
......@@ -693,22 +842,21 @@ export default class EditMailBox extends React.Component {
);
}
let disabledCss = isDisabled ? 'disabled' : '';
//let disabledCss = isDisabled ? 'disabled' : '';
const item = (
<label
className={`radio radio-info radio-inline pretty-input ${disabledCss}`}
className='radio radio-info radio-inline pretty-input'
key={plan}
>
<div className='pretty-radio'>
<input
type='radio'
className={`pretty ${disabledCss}`}
className='pretty'
name='mailbox'
onChange={() => {
this.handleRadioChanged(cos[plan]);
onChange={(e) => {
this.handleRadioChanged(e, cos[plan]);
}}
disabled={isDisabled}
defaultChecked={isChecked}
/>
<span></span>
......
import React from 'react';
import PageInfo from '../page_info.jsx';
import Panel from '../panel.jsx';
import moment from 'moment';
import currencyFormatter from 'currency-formatter';
import EventStore from '../../stores/event_store.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Utils from '../../utils/utils.jsx';
import * as Client from '../../utils/client.jsx';
import ZimbraStore from '../../stores/zimbra_store.jsx';
export default class SalesForm extends React.Component {
constructor(props) {
super(props);
this.handleSetNumbersOfMailboxes = this.handleSetNumbersOfMailboxes.bind(this);
this.getPrices = this.getPrices.bind(this);
this.onlyNumber = this.onlyNumber.bind(this);
this.confirmShipping = this.confirmShipping.bind(this);
this.getDomainInfo = this.getDomainInfo.bind(this);
this.tryAgain = this.tryAgain.bind(this);
this.cos = Utils.getEnabledPlansByCos(ZimbraStore.getAllCos());
this.plans = window.manager_config.plans;
this.keys = Object.keys(this.cos);
this.sales = {};
this.isNAN = false;
this.currency = window.manager_config.invoiceAPI.currency;
const precision = this.currency === 'CLP' ? 0 : 2;
this.currencyParams = {code: this.currency, symbol: '', precision};
this.state = {
loading: true,
errorAjax: false
};
}
confirmShipping(e) {
e.preventDefault();
const plans = [];
const {domain} = this.state;
const domainId = domain.id;
const companyId = domain.attrs.businessCategory;
const adminEmail = UserStore.getCurrentUser().name;
const items = {};
const data = {
domainId,
companyId,
adminEmail,
upgrade: false,
currency: this.currency
};
this.keys.forEach((plan) => {
if (this.sales[plan] && this.sales[plan].quantity && this.sales[plan].quantity > 0) {
items[plan] = this.sales[plan];
items[plan].type = 'Producto';
plans.push(`${Utils.titleCase(plan)} : <strong>${this.sales[plan].quantity}</strong>`);
}
});
if (plans.length < 1) {
return EventStore.emitToast({
type: 'error',
title: 'Compra Casillas',
body: 'Debe indicar cuantas casillas desea comprar.',
options: {
timeOut: 4000,
extendedTimeOut: 2000,
closeButton: true
}
});
}
const content = plans.join(', ');
const total = `${this.refs.total.value} ${this.currency}`;
const options = {
title: 'Confirmación',
text: `Esta seguro de realizar la compra de ${content} por un total de <strong>${total}</strong>`,
html: true,
confirmButtonText: 'Si, compraré',
confirmButtonColor: '#4EA5EC',
showLoaderOnConfirm: true,
closeOnConfirm: false
};
Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
data.items = items;
const requestObject = JSON.stringify(data);
Client.makeSale(requestObject, () => {
Utils.alertToBuy((isConfirmed) => {
if (isConfirmed) {
Utils.handleLink(null, `/domains/${domainId}/mailboxes/new`);
}
}, {
title: 'Compra de Casillas',
text: 'Su compra se ha realizado con éxito.',
showCancelButton: false,
confirmButtonColor: '#4EA5EC',
confirmButtonText: 'Muy bien',
type: 'success'
});
}, (error) => {
Utils.alertToBuy(() => {
return null;
}, {
title: 'Error',
text: error.message || error.error.message || 'Ha ocurrido un error desconocido.',
showCancelButton: false,
confirmButtonColor: '#4EA5EC',
confirmButtonText: 'Entiendo',
type: 'error',
closeOnConfirm: true
});
});
}
}, options);
}
componentWillMount() {
if (!UserStore.isGlobalAdmin()) {
Utils.handleLink(null, '/mailboxes');
}
}
getPrices() {
const {domainId} = this.props.params || this.state.domain.name || null;
const attrs = this.state.domain.attrs;
const {zimbraCreateTimestamp} = attrs;
const {businessCategory} = attrs;
const data = {
domainId,
domainCreatedDate: moment(zimbraCreateTimestamp).format('MM/DD/Y'),
anualRenovation: true,
companyId: businessCategory,
type: 'standar',
currency: this.currency
};
Client.getPrices(data, (success) => {
this.setState({
loading: false,
prices: success.result.prices,
isAnual: success.result.isAnual,
description: success.result.isAnual ? success.result.description : null
});
}, (error) => {
this.setState({
errorAjax: true,
loading: false
});
return EventStore.emitToast({
type: 'error',
title: 'Compras - Precios',
body: error.message || error.error.message || 'Ha ocurrido un error al intentar obtener los precios, vuelva a intentarlo por favor.',
options: {
timeOut: 4000,
extendedTimeOut: 2000,
closeButton: true
}
});
});
}
tryAgain(e) {
e.preventDefault();
this.setState({
loading: true,
errorAjax: false
});
this.getPrices();
}
getDomainInfo() {
const {domainId} = this.props.params;
Client.getDomain(domainId, (res, err) => {
if (err) {
return Utils.alertToBuy(() => {
return null;
}, {
title: 'Error',
text: err.message || err.error.message || 'Ha ocurrido un error desconocido, cuando se recuperaba la información del dominio.',
showCancelButton: false,
confirmButtonColor: '#4EA5EC',
confirmButtonText: 'Entiendo',
type: 'error',
closeOnConfirm: true
});
}
this.setState({
domain: res
});
return this.getPrices();
});
}
componentDidMount() {
GlobalActions.emitEndLoading();
this.getDomainInfo();
}
handleSetNumbersOfMailboxes(e, id) {
if (this.isNAN) {
e.preventDefault();
return null;
}
const amount = e.target.value.trim();
let totalPrice = 0;
let description = 'Nuevas Casillas ';
this.keys.forEach((plan) => {
if (this.cos[plan] === id) {
const price = this.state.prices[plan];
const size = amount.length > 0 ? parseInt(amount, 10) : 0;
const total = size ? size * price : size;
const totalFormatted = total ? currencyFormatter.format(total, this.currencyParams) : total;
this.refs[`${plan}-total`].value = totalFormatted;
description += Utils.titleCase(plan);
this.sales[plan] = {
quantity: size,
description,
price,
id,
total
};
}
if (this.sales[plan] && this.sales[plan].total && this.sales[plan].total > 0) {
totalPrice += this.sales[plan].total;
}
});
const currentTotal = totalPrice ? currencyFormatter.format(totalPrice, this.currencyParams) : totalPrice;
this.refs.total.value = currentTotal;
}
onlyNumber(e) {
const key = e.keyCode;
const forbidden = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 8, 9, 37, 39];
this.isNAN = false;
if (!(forbidden.indexOf(key) > -1)) {
this.isNAN = true;
e.preventDefault();
return null;
}
}
render() {
const plans = this.cos;
const configPlans = this.plans;
const keysPlans = this.keys;
let form = null;
let actions;
const buttons = [];
const {description} = this.state;
let descriptionText;
if (description) {
descriptionText = (
<div
key='desc-key'
className='alert alert-info margin-bottom'
>
<span
className='glyphicon glyphicon glyphicon-question-sign'
aria-hidden='true'
></span>
<span className='sr-only'>Info:</span>
{description}
</div>
);
}
if (this.state.errorAjax) {
form = (
<div
className='text-center'
key={'errorajax-loading'}
>
<i
className='fa fa-refresh fa-4x fa-fw pointer'
onClick={this.tryAgain}
>
</i>
<p>{'Intentarlo de nuevo'}</p>
</div>
);
}
if (this.state.isAnual) {
buttons.push(
{
label: 'Anual',
props: {
className: 'btn btn-success btn-xs'
}
}
);
} else {
buttons.push(
{
label: 'Mensual',
props: {
className: 'btn btn-info btn-xs'
}
}
);
}
if (this.state.loading) {
form = (
<div
className='text-center'
key={'prices-loading'}
>
<i className='fa fa-spinner fa-spin fa-4x fa-fw'></i>
<p>{'Cargando Precios...'}</p>
</div>
);
}
if (!this.state.loading && this.state.prices) {
const prices = this.state.prices;
const rows = keysPlans.map((plan) => {
const cosId = plans[plan];
const salesArray = configPlans[plan].sales;
const fields = salesArray.map((field, index) => {
const label = field.label || `Casillas ${Utils.titleCase(plan)}`;
const price = field.hasPrice ? currencyFormatter.format(prices[plan], this.currencyParams) : '';
const myref = field.ref ? {ref: `${plan}-${field.ref}`} : {};
return (
<div
key={`sale-input-${plan}-${index}`}
className='col-xs-4'
>
<div className='form-group'>
<div className='input-group'>
<div className='input-group-addon'>{label}</div>
<input
type='text'
className='form-control'
disabled={field.disabled}
defaultValue={price}
{...myref}
onKeyUp={(e) => {
this.handleSetNumbersOfMailboxes(e, cosId);
}}
onKeyDown={this.onlyNumber}
/>
</div>
</div>
</div>
);
});
return (
<div
key={`row-fields-${plan}`}
className='row'
>
{fields}
</div>
);
});
form = (
<form key='form-container'>
{rows}
<div className='row'>
<div className='col-xs-4 pull-right'>
<div className='form-group'>
<div className='input-group'>
<div className='input-group-addon'>Total</div>
<input
type='text'
disabled={true}
className='form-control'
ref='total'
/>
<div className='input-group-addon'>{this.currency}</div>
</div>
</div>
</div>
</div>
</form>
);
actions = (
<div
className='row'
key='actions-container'
>
<div className='col-xs-12 text-right'>
<button className='btn btn-default'>Cancelar</button>
<button
className='btn btn-info'
onClick={this.confirmShipping}
>Comprar</button>
</div>
</div>
);
}
return (
<div>
<PageInfo
titlePage='Compras'
descriptionPage='Comprar casillas'
/>
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-12 central-content'>
<Panel
btnsHeader={buttons}
children={[descriptionText, form, actions]}
/>
</div>
</div>
</div>
</div>
);
}
}
SalesForm.propTypes = {
params: React.PropTypes.object
};
......@@ -6,6 +6,16 @@
"zimbraProxy": "https://192.168.1.8:7071",
"dnsApiUrl": "http://zimbra.zboxapp.dev:3000",
"webMailUrl": "https://192.168.1.8:8443",
"salesAPI": {
"base": "http://localhost:8080/parse",
"getPrices": "/functions/getPrices",
"makeSale": "/functions/makeSale",
"appId": "salesZboxManagerApp"
},
"invoiceAPI": {
"currency": "CLP",
"requireTax": true
},
"timeoutRequest": 60000,
"dns": {
"url": "http://zimbra.zboxapp.dev:9081/powerdns_proxy",
......@@ -24,7 +34,22 @@
"statusCos": "btn-success",
"label": "Básica",
"isEnabledToEdit": true,
"forRights": true
"forRights": true,
"sales": [
{
"disabled": false
},
{
"disabled": true,
"label": "Precio",
"hasPrice": true
},
{
"disabled": true,
"label": "$",
"ref": "total"
}
]
},
"premium": {
"statusCos": "btn-primary2",
......@@ -32,13 +57,43 @@
"isEnabledToEdit": true,
"forRights": true,
"archiving": true,
"refer": "archiving"
"refer": "archiving",
"sales": [
{
"disabled": false
},
{
"disabled": true,
"label": "Precio",
"hasPrice": true
},
{
"disabled": true,
"label": "$",
"ref": "total"
}
]
},
"professional": {
"statusCos": "btn-primary",
"label": "Profesional",
"isEnabledToEdit": true,
"forRights": true
"forRights": true,
"sales": [
{
"disabled": false
},
{
"disabled": true,
"label": "Precio",
"hasPrice": true
},
{
"disabled": true,
"label": "$",
"ref": "total"
}
]
},
"default": false,
"archiving": {
......
......@@ -23,6 +23,7 @@ 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 SearchView from './components/search/search.jsx';
import SalesForm from './components/sales/sales.jsx';
import * as Client from './utils/client.jsx';
import * as Utils from './utils/utils.jsx';
......@@ -207,6 +208,11 @@ function renderRootComponent() {
path='search/:query'
component={SearchView}
/>
<Route
path='sales/:domainId/mailboxes'
component={SalesForm}
/>
</Route>
<Route component={NotLoggedIn}>
<IndexRedirect to='login'/>
......
......@@ -1644,3 +1644,9 @@ label {
.overflow {
overflow: hidden;
}
.alert {
&.margin-bottom {
margin-bottom: 15px;
}
}
......@@ -779,3 +779,48 @@ export function deleteRecords(zoneUrl, record, success, error) {
return success(data);
});
}
export function getPrices(data, success, error) {
const appId = window.manager_config.salesAPI.appId;
const endpoints = window.manager_config.salesAPI;
const url = endpoints.base + endpoints.getPrices;
$.ajax({
url: url,
method: 'POST',
data: data,
headers: {
'X-Parse-Application-Id': appId
},
dataType: 'json',
success: function onSuccess(response) {
success(response);
},
error: function onError(err) {
error(err.responseJSON || err);
}
});
}
export function makeSale(data, success, error) {
const appId = window.manager_config.salesAPI.appId;
const endpoints = window.manager_config.salesAPI;
const url = endpoints.base + endpoints.makeSale;
$.ajax({
url: url,
method: 'POST',
data: data,
contentType: 'application/json',
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-REST-API-Key': 'master'
},
dataType: 'json',
success: function onSuccess(response) {
success(response);
},
error: function onError(err) {
error(err.responseJSON || err);
}
});
}
......@@ -5,6 +5,7 @@ import {browserHistory} from 'react-router';
import * as GlobalActions from '../action_creators/global_actions.jsx';
import Constants from './constants.jsx';
import ZimbraStore from '../stores/zimbra_store.jsx';
import sweetAlert from 'sweetalert';
const messageType = Constants.MessageType;
......@@ -140,7 +141,10 @@ export function areMapsEqual(a, b) {
}
export function handleLink(e, path, location) {
if (e) {
e.preventDefault();
}
if (location) {
if (`/${location.pathname}` !== path) {
GlobalActions.emitStartLoading();
......@@ -803,3 +807,37 @@ export function addEventListenerFixed(element, type, callback) {
element.addEventListener(type, callback, typeof (fixEvents[type]) !== 'undefined');
}
export function alertToBuy(callback, options) {
const defaults = {
title: 'Alerta',
type: 'info',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: 'Si, continuar',
closeOnConfirm: true,
showLoaderOnConfirm: false,
animation: 'slide-from-top'
};
const optsExtended = options ? Object.assign(defaults, options) : defaults;
sweetAlert(optsExtended,
(isConfirmed) => {
if (callback && typeof callback === 'function') {
callback(isConfirmed, sweetAlert);
}
}
);
}
export function getDaysFromDate2Date(dateFrom, dateTo) {
// check if it receive and real object date or just string
const from = typeof dateFrom === 'object' ? dateFrom : new Date(dateFrom);
const to = typeof dateTo === 'object' ? dateTo : new Date(dateTo);
// get timestamp of each date and then subtract them. just get the absolute result
var timeDiff = Math.abs(from.getTime() - to.getTime());
// round to up the result, and divide result of subtract with the amount of milliseconds of one day.
var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
return diffDays;
}
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