Commit 4cee35f0 authored by Juorder Antonio's avatar Juorder Antonio

add app sales integrated with manager zbox app, only for global admin, for...

add app sales integrated with manager zbox app, only for global admin, for testing purposes, add sales view. etc
parent e315728f
1466617142
1467382402
\ No newline at end of file
......@@ -58,12 +58,12 @@ stop-server:
kill $$PROCID; \
done
@for PROCID in $$(ps -ef | grep "[b]abel-node.*companies" | awk '{ print $$2 }'); do \
echo stopping server $$PROCID; \
kill $$PROCID; \
done
@for PROCID in $$(ps -ef | grep "[b]abel-node.*sales" | awk '{ print $$2 }'); do \
echo stopping server $$PROCID; \
kill $$PROCID; \
done
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,10 +268,53 @@ export default class CreateMailBox extends React.Component {
});
}
handleRadioChanged(val) {
this.setState({
zimbraCOSId: val
});
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() {
......@@ -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,10 +58,153 @@ export default class EditMailBox extends React.Component {
});
}
handleRadioChanged(val) {
this.setState({
zimbraCOSId: val
});
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() {
......@@ -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) {
e.preventDefault();
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