Initial commit

This commit is contained in:
Senad Uka
2018-06-11 11:09:35 +02:00
commit ed7df7b11f
1954 changed files with 483354 additions and 0 deletions

34
client-wiaas/src/App.js Normal file
View File

@@ -0,0 +1,34 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import ContentContainer from './containers/ContentContainer.jsx';
import LogInContainer from './containers/login/LogInContainer.jsx';
import './App.css';
import './mainComponents/box/WiaasBox.css';
import NotificationBox from './mainComponents/notification/NotificationBox.jsx';
import DialogBox from './mainComponents/dialog/DialogBox.jsx';
class App extends Component {
render() {
const {isLoggedIn, updateMessages, isDialogOpen, dialogContent} = this.props;
return (<div className="App">
{
!isLoggedIn
? (<LogInContainer/>)
: (<ContentContainer className="content"/>)
}
<NotificationBox messages={updateMessages}/>
<DialogBox isDialogOpen={isDialogOpen} dialogContent={dialogContent}/>
</div>);
}
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
updateMessages: state.notificationReducer.updateMessages,
isDialogOpen: state.dialogReducer.isDialogOpen,
dialogContent: state.dialogReducer.dialogContent
});
export default connect(mapStateToProps)(App);

View File

@@ -0,0 +1,6 @@
@import 'styleConstants.scss';
.App {
text-align: left;
height: 100%;
}

View File

@@ -0,0 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});

View File

@@ -0,0 +1,40 @@
import {
API_SERVER
} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_SET_BID,
bidsTexts
} from '../../constants/bidsConstants';
import {fetchCartItems} from '../cart/cartActions';
import {updateMessages} from '../notification/notificationActions';
import {setDialogOpenFlag} from '../../actions/dialog/dialogActions';
const client = new HtmlClient();
const requestSetBid = () => ({
type: REQUEST_SET_BID
});
export const setBid = (idBid, idCart) => {
return dispatch => {
dispatch(requestSetBid());
return client.fetch({
url: `${API_SERVER}/cart/api/setBidForCart`,
method: 'post',
data: {idBid, idCart}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, bidsTexts.messages));
if(response.data.messages[0].code === 'success'){
dispatch(setDialogOpenFlag(false));
dispatch(fetchCartItems(true));
}
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};

View File

@@ -0,0 +1,312 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_SHOP_CART_COUNT,
RECEIVE_SHOP_CART_COUNT,
REQUEST_SHOP_CART_ITEMS,
RECEIVE_SHOP_CART_ITEMS,
SET_CURRENT_STEP,
RESET_STEPS,
GO_TO_NEXT_STEP,
GO_TO_PREVIOUS_STEP,
LOAD_CART_STEPS,
cartSteps,
UPLOAD_DOCUMENT,
REQUEST_CART_DOCUMENTS,
RECEIVE_CART_DOCUMENTS,
REQUEST_CUSTOMER_DETAILS,
RECEIVE_CUSTOMER_DETAILS,
SELECT_COUNTRY_DELIVERY,
SELECT_COUNTRY_BILLING,
SET_ORDER_INFO,
IS_CART_ITEMS_DISABLED,
RECEIVE_ORDER_TOTAL_PRICE,
UPDATE_CART_ITEMS,
SET_NEXT_STEP,
SET_PREV_STEP,
SET_ORDER_PLACED,
SET_ORDER_PLACED_REDIRECT,
cartMessages
} from '../../constants/cartConstants';
import {updateMessages} from '../notification/notificationActions';
const client = new HtmlClient();
export const requestShopCartCount = () => ({type: REQUEST_SHOP_CART_COUNT});
export const receiveShopCartCount = (json) => ({type: RECEIVE_SHOP_CART_COUNT, cartCount: json});
export const requestShopCartItems = () => ({
type: REQUEST_SHOP_CART_ITEMS,
isLoading: true
});
export const receiveShopCartItems = (json) => ({
type: RECEIVE_SHOP_CART_ITEMS,
isLoading: false,
cartItems: json
});
export const requestCustomerDetails = () => ({
type: REQUEST_CUSTOMER_DETAILS,
isLoading: true
});
export const receiveCustomerDetails = (json) => ({
type: RECEIVE_CUSTOMER_DETAILS,
isLoading: false,
customerDetails: json
});
export const fetchCartCount = () => {
return dispatch => {
dispatch(requestShopCartCount());
return client.fetch({url: `${API_SERVER}/cart/api/getCartCount`}).then(response => {
if (typeof response.data !== 'undefined' && 'cartItemsCount' in response.data) {
dispatch(receiveShopCartCount(response.data.cartItemsCount));
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const fetchCartItems = (isForSteps = false) => {
return dispatch => {
dispatch(requestShopCartItems());
return client.fetch({url: `${API_SERVER}/cart/api/getCartItems`}).then(response => {
if (typeof response.data !== 'undefined' && 'cartItems' in response.data) {
dispatch(receiveShopCartItems(response.data.cartItems));
dispatch(fetchCartDocuments(response.data.cartItems.map((cartItem) => cartItem.idPackage), isForSteps));
dispatch(updateOrderTotalPrice(response.data.cartItems));
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
const updateOrderTotalPrice = (cartItems) => ({type: RECEIVE_ORDER_TOTAL_PRICE, cartItems});
const updateCartItems = (newItem)=>(
{type: UPDATE_CART_ITEMS, newItem}
);
export const updateQuantity = (cartItem, quantity, updateCartAllItems) => {
const params = {
idPackage: cartItem.idPackage || 0,
idCustomerInstance: cartItem.idCustomerInstance || 0,
idPrice: cartItem.idPrice || 0,
quantity
};
return dispatch => {
return client.fetch({
url: `${API_SERVER}/cart/api/updateQuantity`,
method: 'post',
data: params
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
const filteredMessages = response.data.messages.filter(message => {
return message.code === 'error';
});
if(filteredMessages.length > 0){
dispatch(updateMessages(filteredMessages, cartMessages));
}else{
cartItem.quantity = quantity;
dispatch(updateCartItems(cartItem));
}
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const removeCartItem = (idCart) => {
return dispatch => {
return client.fetch({
url: `${API_SERVER}/cart/api/removeFromCart`,
method: 'post',
data: {idCart}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, cartMessages));
response.data.messages.forEach(messageObject => {
if (messageObject.code === 'success') {
dispatch(fetchCartCount());
dispatch(fetchCartItems(true));
}
})
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
const getSteps = (whitoutUploadDoc) => {
const newSteps = Object.assign({}, cartSteps());
if(whitoutUploadDoc) {
delete newSteps.cartUploadDocuments;
newSteps.cartCustomerDetails.previous = null;
newSteps.cartCustomerDetails.status = 'active';
}
return {
type: LOAD_CART_STEPS,
cartSteps: newSteps
};
};
export const loadSteps = (whitoutUploadDoc) => {
const firstStep = whitoutUploadDoc ? 'cartCustomerDetails' : 'cartUploadDocuments';
return dispatch => {
dispatch(getSteps(whitoutUploadDoc));
dispatch(selectStep(firstStep));
dispatch(resetSteps());
dispatch(setCartItemsDisabled(false));
}
}
export const selectStep = (step, params={}) => {
return {type: SET_CURRENT_STEP, currentStep: step, params};
}
const resetSteps = () => ({type: RESET_STEPS});
export const getCustomerDetails = () => {
return dispatch => {
return client.fetch({url: `${API_SERVER}/cart/api/getCustomerDetails`})
.then(response => {
if (typeof response.data !== 'undefined' && response.data.customerDetails) {
const customerDetails = response.data.customerDetails;
dispatch(receiveCustomerDetails(customerDetails));
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const nextStep = (step) => ({type: GO_TO_NEXT_STEP});
export const previousStep = (params) => ({type: GO_TO_PREVIOUS_STEP, params});
export const uploadDocumnet = () => ({type: UPLOAD_DOCUMENT});
export const uploadOrderDocument = (idPackage, idDocumentType, file, packages) => {
return dispatch => {
dispatch(uploadDocumnet());
return client.uploadFile(file, {
url: `${API_SERVER}/cart/api/uploadOrderDocument`,
data: {
idDocumentType,
idPackage
}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, cartMessages));
dispatch(fetchCartDocuments(packages));
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const badFile = () =>{
return dispatch => {
dispatch(updateMessages([{code: 'warning', message: 'INVALID_FILE'}], cartMessages));
}
}
export const requestCartDocuments = () => ({
type: REQUEST_CART_DOCUMENTS,
isLoading: true
});
export const receiveCartDocuments = (json) => ({
type: RECEIVE_CART_DOCUMENTS,
isLoading: false,
cartDocuments: json
});
export const fetchCartDocuments = (packages, isForSteps = false) => {
return dispatch => {
dispatch(requestCartDocuments());
return client.fetch({
url: `${API_SERVER}/cart/api/getCartDocuments`,
method: 'post',
data: {packages}
}).then(response => {
if (response.data) {
dispatch(receiveCartDocuments(response.data));
if(isForSteps) {
const whitoutUploadDoc = response.data.templates.length === 0;
dispatch(loadSteps(whitoutUploadDoc));
}
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const selectCountryDelivery = (country) => ({type: SELECT_COUNTRY_DELIVERY, selectedCountryDelivery: country});
export const selectCountryBilling = (country) => ({type: SELECT_COUNTRY_BILLING, selectedCountryBilling: country});
export const saveOrderDetails = (orderDetails, cartItems) => {
const orderInfo = {orderDetails, cartItems};
return dispatch => {
return client.fetch({
url: `${API_SERVER}/cart/api/saveOrderDetails`,
method: 'post',
data: {orderDetails, cartItems}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(getCustomerDetails());
dispatch(setOrderInfo(orderInfo));
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
export const setOrderInfo = (info) => ({type: SET_ORDER_INFO, orderInfo: info});
export const setOrderPlacedFlag = (flag) => ({type: SET_ORDER_PLACED, orderPlaced: flag});
export const setOrderPlacedRedirectFlag = (flag) => ({type: SET_ORDER_PLACED_REDIRECT, orderPlacedRedirect: flag});
export const placeOrder = (orderInfo) => {
const {orderDetails, cartItems} = orderInfo;
return dispatch => {
return client.fetch({
url: `${API_SERVER}/cart/api/placeOrder`,
method: 'post',
data: {orderDetails, cartItems}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, cartMessages));
if(!checkIfErrorMessageExists(response.data.messages)) {
dispatch(fetchCartCount());
dispatch(setOrderPlacedFlag(true));
dispatch(setOrderPlacedRedirectFlag(true));
}
}
}).catch(error => {
client.onError(error, dispatch);
});
}
}
const checkIfErrorMessageExists = (messagesArray) => {
return messagesArray.some(messageObj => {
return messageObj.code === 'error';
});
}
export const setCartItemsDisabled = (flag) => ({type: IS_CART_ITEMS_DISABLED, isCartItemsDisabled: flag});
export const setNextActionFct = (fct) => ({type: SET_NEXT_STEP, nextStepAction: fct});
export const setPrevActionFct = (fct) => ({type: SET_PREV_STEP, prevStepAction: fct});

View File

@@ -0,0 +1,9 @@
import {
SET_PACKAGE_FROM_URL
} from '../../constants/coMarketConstants';
export const setPackageFromUrl = (idPackage) => ({
type: SET_PACKAGE_FROM_URL,
idPackage
});

View File

@@ -0,0 +1,166 @@
import {
API_SERVER
} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import PriceHelper from '../../helpers/coMarket/PriceHelper';
import {
REQUEST_PACKAGE_DETAILS,
RECIEVE_PACKAGE_DETAILS,
SELECT_OPTION,
SELECT_AGREEMENT,
SELECT_ADDITIONAL,
REMOVE_ADDITIONAL,
REQUEST_ADD_TO_CART,
CLEAR_SELECTIONS,
coMarketMessages
} from '../../constants/coMarketConstants';
import {updateMessages} from '../notification/notificationActions';
import {fetchCartCount} from '../cart/cartActions';
const client = new HtmlClient();
const priceHelper = new PriceHelper();
const requestPackageDetails = () => ({
type: REQUEST_PACKAGE_DETAILS,
isLoading: true
});
const recievePackageDetails = (json) => ({
type: RECIEVE_PACKAGE_DETAILS,
isLoading: false,
selectedPackage: json
});
const clearSelections = () => ({
type: CLEAR_SELECTIONS,
selectedOptions: null,
selectedAdditionals: null,
selectedAgreement: null
})
export const fetchPackageDetails = (params) => {
return dispatch => {
dispatch(requestPackageDetails());
return client.fetch({
url: `${API_SERVER}/coMarket/api/getShopPackageDetails`,
method: 'post',
data: params
})
.then(response => {
if(response.data){
const jsonData = response.data;
dispatch(clearSelections());
if(jsonData.prices && jsonData.prices.length){
dispatch(selectAgreement(jsonData.prices[0]));
}
if(jsonData.groups){
Object.keys(jsonData.groups).forEach((idGroup) => {
const defaultOption = jsonData.groups[idGroup].options.find((option) => {return parseInt(option.isDefault, 10) === 1});
if(defaultOption){
dispatch(selectOption(idGroup, defaultOption));
}
});
}
dispatch(recievePackageDetails(jsonData));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
}
const generateOptions = (selectedOptions, selectedAdditionals, selectedAgreement) => {
const extraPackages = [];
const unavailablePackages = [];
if(selectedAdditionals && selectedAdditionals.length){
selectedAdditionals.forEach(additional => {
const selectedPrice = priceHelper.getSelectedPrice(additional, selectedAgreement);
if(!selectedPrice){
unavailablePackages.push(additional);
}else{
extraPackages.push(additional.idAdditionalPackage);
}
});
}
if(selectedOptions){
Object.keys(selectedOptions).forEach(idGroup => {
const selectedPrice = priceHelper.getSelectedPrice(selectedOptions[idGroup], selectedAgreement);
if(!selectedPrice){
unavailablePackages.push(selectedOptions[idGroup]);
}else{
extraPackages.push(selectedOptions[idGroup].idOptionPackage);
}
});
}
return {extraPackages, unavailablePackages};
};
const requestAddToCart = () => ({
type: REQUEST_ADD_TO_CART
});
export const addToCart = (addParams) => {
const options = generateOptions(addParams.selectedOptions, addParams.selectedAdditionals, addParams.selectedAgreement);
const params = {
idPackage: addParams.selectedPackage.packageInfo.idPackage,
idPrice: addParams.selectedAgreement.idPrice,
options: options.extraPackages
};
if(options.unavailablePackages.length){
const unavailable = options.unavailablePackages.map((unavailable) =>{return unavailable.optionName || unavailable.packageName;});
const message = coMarketMessages.UNAVAILABLE_PACKAGES + ' ' + unavailable.join();
return dispatch => {dispatch(updateMessages([{code: 'warning', message}], coMarketMessages))};
}
return dispatch => {
dispatch(requestAddToCart());
return client.fetch({
url: `${API_SERVER}/coMarket/api/addToCart`,
method: 'post',
data: params
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, coMarketMessages));
dispatch(fetchCartCount());
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
export const selectAgreement = (agreement) => {
return {
type: SELECT_AGREEMENT,
selectedAgreement: agreement,
};
};
export const selectOption = (idGroup, option) => {
const selectedOption = {};
selectedOption[idGroup] = option;
return {
type: SELECT_OPTION,
selectedOptions: selectedOption,
};
};
export const selectAdditional = (additional) => ({
type: SELECT_ADDITIONAL,
selectedAdditonal: additional,
});
export const removetAdditional = (additional) => ({
type: REMOVE_ADDITIONAL,
selectedAdditonal: additional,
});

View File

@@ -0,0 +1,86 @@
import {
API_SERVER
} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_SHOP_PACKAGES,
RECIEVE_SHOP_PACKAGES,
REQUEST_SHOP_COMMERCIAL_LEADS,
RECIEVE_SHOP_COMMERCIAL_LEADS,
SELECT_SHOP_COMMERCIAL_LEAD
} from '../../constants/coMarketConstants';
const client = new HtmlClient();
const requestShopPackages = () => ({
type: REQUEST_SHOP_PACKAGES,
isLoading: true
});
const recieveShopPackages = (json) => ({
type: RECIEVE_SHOP_PACKAGES,
isLoading: false,
shopPackages: json
});
export const fetchShopPackages = (cl, search) => {
return dispatch => {
dispatch(requestShopPackages());
return client.fetch({
url: `${API_SERVER}/coMarket/api/getShopPackages`,
method: 'post',
data: {
idCommercialLead: (cl && cl.value) || 0,
search
}
})
.then(response => {
if(response.data && response.data.packages){
dispatch(recieveShopPackages(response.data.packages))
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
}
const requestShopCommercialLeads = () => ({
type: REQUEST_SHOP_COMMERCIAL_LEADS
});
const recieveShopCommercialLeads = (json) => ({
type: RECIEVE_SHOP_COMMERCIAL_LEADS,
commercialLeads: json
});
const generateClOptions = (commercialLeads) => {
commercialLeads.forEach((cl) => {
cl.value = cl.idCommercialLead;
cl.label = cl.commercialLeadName;
});
return commercialLeads;
}
export const fetchShopCommercialLeads = () => {
return dispatch => {
dispatch(requestShopCommercialLeads());
return client.fetch({url: `${API_SERVER}/coMarket/api/getAllCommercialLeads`})
.then(response => {
if(response.data && response.data.commercialLeads){
const clOptions = generateClOptions(response.data.commercialLeads);
dispatch(recieveShopCommercialLeads(clOptions));
if (clOptions.length) {
dispatch(selectCommercialLead(clOptions[0]));
dispatch(fetchShopPackages(clOptions[0]));
}
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
}
export const selectCommercialLead = (cl) => ({
type: SELECT_SHOP_COMMERCIAL_LEAD,
selectedCommercialLead: cl
});

View File

@@ -0,0 +1,27 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {REQUEST_GADGETS, RECIEVE_GADGETS} from '../../constants/dashboardConstants';
const htmlClient = new HtmlClient();
const requestGadgets = () => ({type: REQUEST_GADGETS});
const recieveGadgets = (json) => ({
type: RECIEVE_GADGETS,
gadgets: json
});
export const fetchGadgets = () => {
return dispatch => {
dispatch(requestGadgets());
return htmlClient.fetch({
url: `${API_SERVER}/dashboards/api/getMyDashboard`
})
.then(response => {
if(response.data && response.data.gadgets){
dispatch(recieveGadgets(response.data.gadgets))
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,28 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {REQUEST_NEXT_ACTIONS, RECIEVE_NEXT_ACTIONS} from '../../constants/dashboardConstants';
const client = new HtmlClient();
export const requestNextActions = () => ({
type: REQUEST_NEXT_ACTIONS,
isLoading: true
});
export const recieveNextActions = (json) => ({
type: RECIEVE_NEXT_ACTIONS,
isLoading: false,
nextActions: json
});
export const fetchNextActions = () => {
return dispatch => {
dispatch(requestNextActions());
return client.fetch({
url: `${API_SERVER}/dashboards/api/getNextActionsInfo`
})
.then(response => dispatch(recieveNextActions(response.data)))
.catch(error => {
client.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,35 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_ORDERS,
RECEIVE_ORDERS
} from '../../constants/dashboardConstants';
const htmlClient = new HtmlClient();
export const requestOrders = () => ({
type: REQUEST_ORDERS,
isLoading: true
});
export const recieveOrders = (json) => ({
type: RECEIVE_ORDERS,
isLoading: false,
orders: json
});
export const fetchOrders = (viewAllOrders) => {
return dispatch => {
dispatch(requestOrders());
return htmlClient.fetch({
url: `${API_SERVER}/dashboards/api/getOrderCentralInfo`,
method: 'post',
data: {
viewAllOrders
}
})
.then(response => dispatch(recieveOrders(response.data)))
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,4 @@
import {CONTENT_MESSAGE, IS_OPENED} from '../../constants/dialogConstants';
export const setDialogContent = (json) => ({type: CONTENT_MESSAGE, dialogContent: json});
export const setDialogOpenFlag = (flag) => ({type: IS_OPENED, isDialogOpen: flag});

View File

@@ -0,0 +1,268 @@
import jwtDecode from 'jwt-decode';
import {
API_SERVER
} from '../../config';
import {
LOGIN,
LOGOUT,
LOGIN_SUCCESS,
LOGIN_FAIL,
VALIDATE_TOKEN,
REQUEST_MODULES,
RECIEVE_MODULES,
REQUEST_FORGOT_PASSWORD,
FORGOT_PASSWORD,
REFRESH_TOKEN,
REQUEST_CHANGE,
PASSWORD_CHANGED,
SET_COMPANY_ADMIN_FLAG,
authActivity
} from '../../constants/authConstants';
import HtmlClient from '../../helpers/HtmlClient';
const htmlClient = new HtmlClient();
let refreshToken = '';
let refreshTimer = {};
const REFRESH_TIME = 1000 * 60 * 50; //refresh 10 minutes before expired
export const login = () => ({
type: LOGIN
});
export const validateToken = () => ({
type: VALIDATE_TOKEN
});
export const validateAccessToken = (token) => {
return dispatch => {
dispatch(validateToken());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/validateToken`
})
.then(response => {
if (response.data && response.data.status === 'success') {
const serverTime = response.data.serverTime || 1;
dispatch(loggedIn({
accessToken: token,
userInfo: response.data.userInfo
}));
refreshToken = response.data.refreshToken;
startRefreshTimer(dispatch, serverTime);
dispatch(setUserAsCompanyAdmin(response.data.userInfo.wiaas_is_company_admin));
} else {
dispatch(loginFail(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const setUserAsCompanyAdmin = (isCompanyAdmin) => ({type: SET_COMPANY_ADMIN_FLAG, isCompanyAdmin});
export const validateCredentials = (username, password) => {
return dispatch => {
dispatch(login());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/getToken`,
method: 'post',
data: {
username,
password,
login: true
},
header: {}
})
.then(response => {
if (response.data.status === 'success') {
const decodedAceessToken = jwtDecode(response.data.accessToken);
if(decodedAceessToken.data.wiaas_user_type === 'customer'){
localStorage.setItem('accessToken', response.data.accessToken);
const serverTime = response.data.serverTime || 1;
refreshToken = response.data.refreshToken;
startRefreshTimer(dispatch, serverTime);
dispatch(loggedIn(response.data));
dispatch(setUserAsCompanyAdmin(response.data.userInfo.wiaas_is_company_admin));
}else{
dispatch(loginFail({status: 'fail', errorMessage: 'INVALID_USER_TYPE'}));
}
} else {
dispatch(loginFail(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const startRefreshTimer = (dispatch, serverTime) => {
const decodedAceessToken = jwtDecode(localStorage.accessToken);
const TEN_MINUTES = 600;
const tokenTimeLeft = decodedAceessToken.exp - serverTime;
const refreshTime = tokenTimeLeft ? (tokenTimeLeft - TEN_MINUTES) * 1000 : REFRESH_TIME;
if(refreshTime <= 0){
dispatch(validateRefreshToken());
}else{
refreshTimer = setTimeout(()=>{
dispatch(validateRefreshToken());
}, refreshTime);
}
}
const requestRefreshToken = () => ({
type: REFRESH_TOKEN
});
const validateRefreshToken = () => {
return dispatch => {
dispatch(requestRefreshToken());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/refreshToken`,
method: 'post',
data: {
refreshToken,
lastActivity: authActivity.lastActivity
}
})
.then(response => {
if (response.data.status === 'success') {
localStorage.setItem('accessToken', response.data.accessToken);
const serverTime = response.data.serverTime || 1;
refreshToken = response.data.refreshToken;
dispatch(setUserAsCompanyAdmin(response.data.userInfo.wiaas_is_company_admin));
startRefreshTimer(dispatch, serverTime);
} else {
dispatch(logout(response.data));
dispatch(loginFail(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const getModules = () => {
return dispatch => {
dispatch(requestModules());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/getModules`,
})
.then(response => {
dispatch(recieveModules(response.data));
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestModules = () => ({
type: REQUEST_MODULES
});
const recieveModules = (json) => ({
type: RECIEVE_MODULES,
modules: json.modules
});
export const logout = () => {
localStorage.removeItem('accessToken');
clearInterval(refreshTimer);
return {
type: LOGOUT,
isLoggedIn: false,
errorMessage: 'LOGGED_OUT'
}
}
export const loggedIn = (jsonData) => {
return {
type: LOGIN_SUCCESS,
isLoggedIn: true,
userInfo: jsonData.userInfo
}
}
export const loginFail = (jsonData) => {
return {
type: LOGIN_FAIL,
isLoggedIn: false,
errorMessage: jsonData.errorMessage
}
}
export const generatePassword = (mail) => {
return dispatch => {
dispatch(requestForgotPassword());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/forgotPassword`,
method: 'post',
data: {mail},
header: {}
})
.then(response => {
if(typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(forgotPasswordMessage(response.data.messages[0]));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestForgotPassword = () => ({
type: REQUEST_FORGOT_PASSWORD,
errorMessage: 'FORGOT_REQUEST_SENT'
});
const forgotPasswordMessage = (jsonData) => {
return {
type: FORGOT_PASSWORD,
errorMessage: jsonData.message,
messageColor: jsonData.code
}
}
const requestChange = () => ({
type: REQUEST_CHANGE
});
const passwordChanged = (messageObj) => {
const code = messageObj.code === 'error' ? 'danger' : messageObj.code;
const isPasswordChanged = messageObj.message === 'PASSWORD_GENERATED' ? true : false;
return {
type: PASSWORD_CHANGED,
errorMessage: messageObj.message,
messageColor: code,
isPasswordChanged: isPasswordChanged
}
};
export const changePassword = (token, newPassword, confirmPassword) => {
return dispatch => {
dispatch(requestChange());
return htmlClient.fetch({
url: `${API_SERVER}/login/api/changePassword`,
method: 'post',
data: {token, newPassword, confirmPassword},
header: {}
})
.then(response => {
if(response.data.messages && response.data.messages.length > 0){
dispatch(passwordChanged(response.data.messages[0]));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,4 @@
import {UPDATE_MESSAGES, DESTROY_MESSAGE} from '../../constants/notificationConstants';
export const destroyMessage = () => ({type: DESTROY_MESSAGE, messages: []});
export const updateMessages = (json, messageObj) => ({type: UPDATE_MESSAGES, updateMessages: {messageJson: json, messageObj}});

View File

@@ -0,0 +1,77 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_ORDER_PROJECTS,
RECEIVE_ORDER_PROJECTS,
REQUEST_ADD_PROJECT,
orderProjectsTexts
} from '../../constants/orderProjectsConstants';
import {updateMessages} from '../notification/notificationActions';
import {setDialogOpenFlag} from '../dialog/dialogActions';
const htmlClient = new HtmlClient();
const requestOrderProjects = () => ({
type: REQUEST_ORDER_PROJECTS,
isLoading: true
});
const recieveOrderProjects = (json) => ({
type: RECEIVE_ORDER_PROJECTS,
isLoading: false,
orderProjects: json
});
const generateOptions = (orderProjects) => {
orderProjects.forEach((orderProject) => {
orderProject.value = orderProject.idProject;
orderProject.label = orderProject.projectName;
});
return orderProjects;
}
export const getOrderProjects = () => {
return dispatch => {
dispatch(requestOrderProjects());
return htmlClient.fetch({
url: `${API_SERVER}/orderProjects/api/getOrderProjects`
})
.then(response => {
if (response.data) {
const orderProjects = generateOptions(response.data);
dispatch(recieveOrderProjects(orderProjects));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestAddProject = () => ({
type: REQUEST_ADD_PROJECT,
isLoading: true
});
export const addProject = (projectData) => {
return dispatch => {
dispatch(requestAddProject());
return htmlClient.fetch({
url: `${API_SERVER}/orderProjects/api/addOrderProject`,
method: 'post',
data: {projectData: JSON.stringify(projectData)}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, orderProjectsTexts.messages));
if(response.data.messages[0].code === 'success'){
dispatch(getOrderProjects());
dispatch(setDialogOpenFlag(false));
}
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,102 @@
import {
API_SERVER
} from '../../config';
import {
REQUEST_CUSTOMER_ACCEPTANCE,
RECEIVE_CUSTOMER_ACCEPTANCE,
UPLOAD_CUSTOMER_ACCEPTANCE,
SEND_CUSTOMER_ACCEPTANCE,
orderMessages
} from '../../constants/ordersConstants';
import {
updateMessages
} from '../notification/notificationActions';
import HtmlClient from '../../helpers/HtmlClient';
const htmlClient = new HtmlClient();
const requestCustomerAcceptance = () => ({
type: REQUEST_CUSTOMER_ACCEPTANCE
});
const recieveCustomerAcceptance = (json) => ({
type: RECEIVE_CUSTOMER_ACCEPTANCE,
customerAcceptance: json
});
export const fetchCustomerAcceptance = (idOrder) => {
return dispatch => {
dispatch(requestCustomerAcceptance());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getCustomerAcceptance`,
method: 'post',
data: {
idOrder
}
})
.then(response => {
if (response.data) {
dispatch(recieveCustomerAcceptance(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const uploadAcceptanceAction = () => ({
type: UPLOAD_CUSTOMER_ACCEPTANCE
});
export const uploadAcceptance = (idOrder, file) => {
return dispatch => {
dispatch(uploadAcceptanceAction());
return htmlClient.uploadFile(file, {
url: `${API_SERVER}/orders/api/uploadAcceptanceDocument`,
data: {
idOrder
}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
dispatch(fetchCustomerAcceptance(idOrder));
}
}).catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const badFile = () =>{
return dispatch => {
dispatch(updateMessages([{code: 'warning', message: 'INVALID_FILE_ACCEPTANCE'}], orderMessages));
}
}
const sendCustomerAcceptance = () => ({
type: SEND_CUSTOMER_ACCEPTANCE
});
export const acceptDeclineInstallation = (idOrder, actionType, declineReason) => {
return dispatch => {
dispatch(sendCustomerAcceptance());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/acceptDeclineInstallation`,
method: 'post',
data: {
idOrder,
actionType,
declineReason
}
})
.then(response => {
if (response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
dispatch(fetchCustomerAcceptance(idOrder));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,66 @@
import {API_SERVER} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_ACTIVE_ORDERS,
RECEIVE_ACTIVE_ORDERS,
REQUEST_HISTORY_ORDERS,
RECEIVE_HISTORY_ORDERS,
SET_VIEW_ALL_ORDERS
} from '../../constants/ordersConstants';
const htmlClient = new HtmlClient();
const requestActiveOrders = () => ({
type: REQUEST_ACTIVE_ORDERS,
isLoading: true
});
const recieveActiveOrders = (json) => ({
type: RECEIVE_ACTIVE_ORDERS,
isLoading: false,
activeOrders: json
});
export const getActiveOrders = () => {
return dispatch => {
dispatch(requestActiveOrders());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getActiveOrders`
})
.then(response => {
if (typeof response.data !== 'undefined' && 'orders' in response.data) {
dispatch(recieveActiveOrders(response.data.orders));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestHistoryOrders = () => ({
type: REQUEST_HISTORY_ORDERS,
isLoading: true
});
const recieveHistoryOrders = (json) => ({
type: RECEIVE_HISTORY_ORDERS,
isLoading: false,
historyOrders: json
});
export const getHistoryOrders = () => {
return dispatch => {
dispatch(requestHistoryOrders());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getHistoryOrders`
})
.then(response => {
if (typeof response.data !== 'undefined' && 'orders' in response.data) {
dispatch(recieveHistoryOrders(response.data.orders));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const setViewAllOrdersFlag = (isViewAllOrdersChecked) => ({type: SET_VIEW_ALL_ORDERS, isViewAllOrdersChecked});

View File

@@ -0,0 +1,344 @@
import {
API_SERVER
} from '../../config';
import {
orderMessages,
REQUEST_ORDER_INFO,
RECEIVE_ORDER_INFO,
SEND_ORDER_COMMENT,
REQUEST_CUSTOMER_DOCUMENTS,
RECEIVE_CUSTOMER_DOCUMENTS,
RE_UPLOAD_DOCUMENT,
REQUEST_VALIDATION_COMMENTS,
RECEIVE_VALIDATION_COMMENTS,
REQUEST_IS_NEXT_STEP_WANTED,
RECEIVE_IS_NEXT_STEP_WANTED,
RECEIVE_IS_COMPONENT_DISABLED,
SET_EARLIEST_INSTALLATION_DATE,
SET_CONFIRMATION_DATES,
REQUEST_INSTALLATION_COMPANIES,
RECEIVE_INSTALLATION_COMPANIES,
REQUEST_ALL_SHIPPING_DATES_CONFIRMED,
RECEIVE_ALL_SHIPPING_DATES_CONFIRMED,
SET_SUPPORT_MESSAGE,
SET_SCHEDULING_DISABLED_FLAG
} from '../../constants/ordersConstants';
import HtmlClient from '../../helpers/HtmlClient';
import {updateMessages} from '../notification/notificationActions';
const htmlClient = new HtmlClient();
const requestOrderInfo = () => ({
type: REQUEST_ORDER_INFO,
isLoading: true
});
const recieveOrderInfo = (json) => ({
type: RECEIVE_ORDER_INFO,
isLoading: false,
orderInfo: json
});
export const fetchOrderInfo = (idOrder) => {
return dispatch => {
dispatch(requestOrderInfo());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getOrderInfo`,
method: 'post',
data: {
idOrder
}
})
.then(response => {
if (response.data) {
dispatch(recieveOrderInfo(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const sendComment = () => ({
type: SEND_ORDER_COMMENT
})
export const addComment = (idOrder, comment) => {
return dispatch => {
dispatch(sendComment());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/addOrderComment`,
method: 'post',
data: {
idOrder,
comment
}
})
.then(response => {
if (response.data && response.data.messages) {
dispatch(updateMessages(response.data.messages, orderMessages));
dispatch(fetchOrderInfo(idOrder));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestCustomerDocuments = () => ({
type: REQUEST_CUSTOMER_DOCUMENTS
});
const recieveCustomerDocuments = (json) => ({
type: RECEIVE_CUSTOMER_DOCUMENTS,
customerDocuments: json
});
export const fetchCustomerDocuments = (idOrder, documentType, idPackage = 0) => {
return dispatch => {
dispatch(requestCustomerDocuments());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getOrderDocumentsPerType`,
method: 'post',
data: {
idOrder,
idPackage,
documentType
}
})
.then(response => {
if (typeof response.data !== 'undefined' && 'documents' in response.data) {
dispatch(recieveCustomerDocuments(response.data.documents));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestValidationComments = () => ({
type: REQUEST_VALIDATION_COMMENTS
});
const recieveValidationComments = (json) => ({
type: RECEIVE_VALIDATION_COMMENTS,
validationComments: json
});
export const fetchValidationComments = (idOrder, idProcessStep, commentType) => {
return dispatch => {
dispatch(requestValidationComments());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getCommentsByType`,
method: 'post',
data: {
idOrder,
idProcessStep,
commentType
}
})
.then(response => {
if (response.data) {
dispatch(recieveValidationComments(response.data));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const reUploadDocumnet = () => ({
type: RE_UPLOAD_DOCUMENT
});
export const reUploadOrderDocument = (idPackage, idOrder, idDocument, file) => {
return dispatch => {
dispatch(reUploadDocumnet());
return htmlClient.uploadFile(file, {
url: `${API_SERVER}/orders/api/reUploadQuestionaire`,
data: {
idPackage,
idOrder,
idDocument
}
}).then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
dispatch(fetchCustomerDocuments(idOrder, 'orderQuestionaire', idPackage));
}
}).catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const badFile = () =>{
return dispatch => {
dispatch(updateMessages([{code: 'warning', message: 'INVALID_FILE_QUESTIONNAINRE'}], orderMessages));
}
}
export const requestIsNextStepWanted = () => ({type: REQUEST_IS_NEXT_STEP_WANTED});
export const receiveIsNextStepWanted = (json) => ({
type: RECEIVE_IS_NEXT_STEP_WANTED,
isNextStepWanted: json
});
const receiveIsComponentDisabled = (isComponentDisabled) => ({
type: RECEIVE_IS_COMPONENT_DISABLED,
isComponentDisabled
});
const setEarliestInstallationDate = (earliestInstallDate) => ({
type: SET_EARLIEST_INSTALLATION_DATE,
earliestInstallDate
});
const setConfirmationDates = (json) => ({
type: SET_CONFIRMATION_DATES,
confirmationDates: json
});
export const getInstallationConfirmationDates = (idOrder, idPackage = 0) => {
return dispatch => {
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getInstallationDates`,
method: 'post',
data: {idOrder, idPackage}
})
.then(response => {
if (typeof response.data !== 'undefined' && 'confirmationDates' in response.data) {
dispatch(setConfirmationDates(response.data.confirmationDates));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
const requestInstallationCompanies = () => ({type: REQUEST_INSTALLATION_COMPANIES});
const recieveInstallationCompanies = (json) => ({
type: RECEIVE_INSTALLATION_COMPANIES,
installCompanies: json
});
const requestAreAllShippingDatesConfirmed = ()=> ({type: REQUEST_ALL_SHIPPING_DATES_CONFIRMED});
const receiveAreAllShippingDatesConfirmed = (json) => ({
type: RECEIVE_ALL_SHIPPING_DATES_CONFIRMED,
areAllShippingDatesConfirmed: json
});
export const getAllDataForInstallation = (idOrder, usedForDirective, stepsNameForInstallation, fileType) => {
const isNextStepTheOneWanted = {};
return dispatch => {
dispatch(requestInstallationCompanies());
dispatch(requestIsNextStepWanted());
dispatch(requestCustomerDocuments());
dispatch(requestAreAllShippingDatesConfirmed());
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/getAllDataForInstallation`,
method: 'post',
data: {idOrder, stepsNameForInstallation, fileType}
})
.then(response => {
if (typeof response.data !== 'undefined') {
if ('messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
}
if('installCompanies' in response.data) {
dispatch(recieveInstallationCompanies(response.data.installCompanies));
}
if('earliestInstallationDate' in response.data) {
dispatch(setEarliestInstallationDate(response.data.earliestInstallationDate));
}
if ('installationDates' in response.data) {
dispatch(setConfirmationDates(response.data.installationDates.confirmationDates));
}
if('isNextStepWanted' in response.data) {
isNextStepTheOneWanted[usedForDirective] = response.data.isNextStepWanted;
dispatch(receiveIsNextStepWanted(isNextStepTheOneWanted));
dispatch(receiveIsComponentDisabled(isNextStepTheOneWanted));
}
if('areAllShippingDatesConfirmed' in response.data) {
dispatch(receiveAreAllShippingDatesConfirmed(response.data.areAllShippingDatesConfirmed));
}
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
};
}
export const updateInstallationDate = (idOrder, idPackage, installationDate, status, clearDateInput='') => {
return dispatch => {
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/updateInstallationDate`,
method: 'post',
data: {idOrder, idPackage, installationDate, status}
})
.then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
if(response.data.messages.find(messageObj => {return messageObj.code === 'success';})) {
if(clearDateInput) {
clearDateInput(undefined);
}
}
}
dispatch(getInstallationConfirmationDates(idOrder, idPackage));
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const removeMyDate = (idOrder, idPackage, installationDate) => {
return dispatch => {
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/removeMyDate`,
method: 'post',
data: {idOrder, idPackage, installationDate}
})
.then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
}
dispatch(getInstallationConfirmationDates(idOrder, idPackage));
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const setSupportMessage = (message) => ({
type: SET_SUPPORT_MESSAGE,
supportText: message
});
export const sendSupportMail = (orderInfo, orderPackages, supportText) => {
return dispatch => {
return htmlClient.fetch({
url: `${API_SERVER}/orders/api/sendSupportMail`,
method: 'post',
data: {orderInfo, orderPackages, supportText}
})
.then(response => {
if (typeof response.data !== 'undefined' && 'messages' in response.data) {
dispatch(updateMessages(response.data.messages, orderMessages));
}
})
.catch(error => {
htmlClient.onError(error, dispatch);
});
}
}
export const setSchedulingFlag = (flag) => ({
type: SET_SCHEDULING_DISABLED_FLAG,
isSchedulingDisabled: flag
});

View File

@@ -0,0 +1,30 @@
import {
SET_PARAMS_FROM_URL,
RESET_PARAMS,
SET_ACTIVE_MODULE,
SET_ACTIVE_SUB_MODULE,
RESET_ACTIVE_SUB_MODULE
} from '../../constants/pageConstants';
export const setParamsFromUrl = (params) => ({
type: SET_PARAMS_FROM_URL,
urlParams: params
});
export const resetParamsFromUrl = () => ({
type: RESET_PARAMS
});
export const setActiveModule = (module) => ({
type: SET_ACTIVE_MODULE,
activeModule: module
});
export const setActiveSubModule = (submodule) => ({
type: SET_ACTIVE_SUB_MODULE,
activeSubmodule: submodule
});
export const resetActiveSubModule = () => ({
type: RESET_ACTIVE_SUB_MODULE
});

View File

@@ -0,0 +1,118 @@
import {
API_SERVER
} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_SAVE_PROFILE_ADDRESS,
REQUEST_REMOVE_PROFILE_ADDRESS,
REQUEST_REMOVE_BILLING_ADDRESS,
REQUEST_SAVE_BILLING_ADDRESS,
profileTexts
} from '../../constants/profileSettingsConstants';
import {fetchProfileInfo} from './profileSettingsActions';
import {updateMessages} from '../notification/notificationActions';
import {setDialogOpenFlag} from '../dialog/dialogActions';
const client = new HtmlClient();
const requestSaveAddress = () => ({
type: REQUEST_SAVE_PROFILE_ADDRESS
});
export const saveProfileAddress = (idUser, profileAddress) => {
return dispatch => {
dispatch(requestSaveAddress());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/saveProfileAddress`,
method: 'post',
data: {profileAddress: JSON.stringify(profileAddress)}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
if(response.data.messages[0].code === 'success'){
dispatch(fetchProfileInfo(idUser));
dispatch(setDialogOpenFlag(false));
}
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
const requestRemoveAddress = () => ({
type: REQUEST_REMOVE_PROFILE_ADDRESS
});
export const removeProfileAddress = (idUser, idProfileAddress) => {
return dispatch => {
dispatch(requestRemoveAddress());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/removeProfileAddress`,
method: 'post',
data: {idProfileAddress}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
dispatch(fetchProfileInfo(idUser));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
}
const requestSaveBillingAddress = () => ({
type: REQUEST_SAVE_BILLING_ADDRESS
});
export const saveBillingAddress = (idUser, idCompany, billingAddress) => {
return dispatch => {
dispatch(requestSaveBillingAddress());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/saveBillingAddress`,
method: 'post',
data: {idCompany, billingAddress: JSON.stringify(billingAddress)}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
if(response.data.messages[0].code === 'success'){
dispatch(fetchProfileInfo(idUser));
dispatch(setDialogOpenFlag(false));
}
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
const requestRemoveBillingAddress = () => ({
type: REQUEST_REMOVE_BILLING_ADDRESS
});
export const removeBillingAddress = (idUser, idBillingAddress) => {
return dispatch => {
dispatch(requestRemoveBillingAddress());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/removeBillingAddress`,
method: 'post',
data: {idBillingAddress}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
dispatch(fetchProfileInfo(idUser));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
}

View File

@@ -0,0 +1,123 @@
import {
API_SERVER
} from '../../config';
import HtmlClient from '../../helpers/HtmlClient';
import {
REQUEST_PROFILE_INFO,
RECIEVE_PROFILE_INFO,
REQUEST_SAVE_PROFILE,
REQUEST_SAVE_COMPANY,
REQUEST_COUNTRIES,
RECIEVE_COUNTRIES,
profileTexts
} from '../../constants/profileSettingsConstants';
import {updateMessages} from '../notification/notificationActions';
const client = new HtmlClient();
const requestProfileInfo = () => ({
type: REQUEST_PROFILE_INFO,
isLoading: true
});
export const recieveProfileInfo = (json) => ({
type: RECIEVE_PROFILE_INFO,
isLoading: false,
profileInfo: json
});
export const fetchProfileInfo = (idUser) => {
return dispatch => {
dispatch(requestProfileInfo());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/getProfileInfo`,
method: 'post',
data: {idUser}
})
.then(response => {
if(response.data){
dispatch(recieveProfileInfo(response.data));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
const requestSaveProfile = () => ({
type: REQUEST_SAVE_PROFILE
});
export const saveProfileInfo = (idUser, profile) => {
return dispatch => {
dispatch(requestSaveProfile());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/saveProfileInfo`,
method: 'post',
data: {idUser, profile: JSON.stringify(profile)}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
dispatch(fetchProfileInfo(idUser));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
const requestSaveCompany = () => ({
type: REQUEST_SAVE_COMPANY
});
export const saveCompanyInfo = (idUser, companyInfo) => {
return dispatch => {
dispatch(requestSaveCompany());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/saveCompanyInfo`,
method: 'post',
data: {companyInfo: JSON.stringify(companyInfo)}
})
.then(response => {
if(response.data && response.data.messages){
dispatch(updateMessages(response.data.messages, profileTexts.messages));
dispatch(fetchProfileInfo(idUser));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};
const requestCountries = () => ({
type: REQUEST_COUNTRIES,
areCountriesLoading: true
});
const recieveCountries = (json) => ({
type: RECIEVE_COUNTRIES,
areCountriesLoading: false,
countries: json
});
export const fetchCountries = () => {
return dispatch => {
dispatch(requestCountries());
return client.fetch({
url: `${API_SERVER}/profileSettings/api/getCoutnries`,
method: 'get'
})
.then(response => {
if(response.data){
dispatch(recieveCountries(response.data));
}
})
.catch(error => {
client.onError(error, dispatch);
});
}
};

View File

@@ -0,0 +1,19 @@
const APPLICAITON_MODE = 'DEV';
const APPLICATION_NAME = 'Co-Market';
const API_VERSION = 'v2';
const API_SERVER_BASE = (() => {
if(APPLICAITON_MODE === 'TEST'){
return 'http://localhost/api-wiaas';
}
if(APPLICAITON_MODE === 'PROD'){
return 'http://localhost/api-wiaas';
}
return 'http://localhost/api-wiaas';
})();
const API_SERVER = API_SERVER_BASE + '/' + API_VERSION;
export {APPLICAITON_MODE, API_SERVER_BASE, API_SERVER, APPLICATION_NAME}

View File

@@ -0,0 +1,31 @@
import {
createStore,
applyMiddleware
} from 'redux';
import thunkMiddleware from 'redux-thunk';
import {
createLogger
} from 'redux-logger';
import reduxCatch from 'redux-catch';
import wiaasReducers from './reducers';
const loggerMiddleware = createLogger();
const errorHandler = (error, getState, lastAction, dispatch) => {
console.error(error);
console.debug('current state', getState());
console.debug('last action was', lastAction);
};
function configureStore() {
return createStore(
wiaasReducers,
applyMiddleware(
reduxCatch(errorHandler),
thunkMiddleware,
loggerMiddleware
)
)
}
export let store = configureStore();

View File

@@ -0,0 +1,33 @@
import DashboardsContainer from '../containers/dashboard/DashboardsContainer.jsx';
import OrdersContainer from '../containers/orders/OrdersContainer.jsx';
import CoMarketContainer from '../containers/coMarket/CoMarketContainer.jsx';
import CartContainer from '../containers/cart/CartContainer.jsx';
import TermsContainer from '../containers/terms/TermsContainer.jsx';
import ProfileSettingsContainer from '../containers/profileSettings/ProfileSettingsContainer.jsx';
export const MainContainers = {
Dashboards: {
container: DashboardsContainer,
params: []
},
Orders: {
container: OrdersContainer,
params: ['idOrder']
},
CoMarket: {
container: CoMarketContainer,
params: ['idCommercialLead', 'idPackage']
},
Cart: {
container: CartContainer,
params: []
},
Terms: {
container: TermsContainer,
params: []
},
ProfileSettings: {
container: ProfileSettingsContainer,
params: []
}
};

View File

@@ -0,0 +1,68 @@
import {API_SERVER_BASE} from '../config.js';
const MODULE = 'AUTH_';
export const LOGIN = MODULE + 'LOGIN';
export const LOGOUT = MODULE + 'LOGOUT';
export const LOGIN_SUCCESS = MODULE + 'LOGIN_SUCCESS';
export const LOGIN_FAIL = MODULE + 'LOGIN_FAIL';
export const REQUEST_FORGOT_PASSWORD = MODULE + 'REQUEST_FORGOT_PASSWORD';
export const FORGOT_PASSWORD = MODULE + 'FORGOT_PASSWORD';
export const VALIDATE_TOKEN = MODULE + 'VALIDATE_TOKEN';
export const REQUEST_MODULES = MODULE + 'REQUEST_MODULES';
export const RECIEVE_MODULES = MODULE + 'RECIEVE_MODULES';
export const REFRESH_TOKEN = MODULE + 'REFRESH_TOKEN';
export const REQUEST_CHANGE = MODULE + 'REQUEST_CHANGE';
export const PASSWORD_CHANGED = MODULE + 'PASSWORD_CHANGED';
export const SET_COMPANY_ADMIN_FLAG = MODULE + 'SET_COMPANY_ADMIN_FLAG';
export const authActivity = {
lastActivity : Date.now()
};
export const loginMessages = {
INVALID_USERNAME_PASSWORD: 'Invalid username or password',
EXPIRED_TOKEN: 'Your session has expired',
LOGGED_OUT: 'You have been successfully logged out !',
NO_USER: 'Please review the mail address and try again!',
GENERATED_SUCCESSFULLY: 'An email was sent to you. Please check your inbox to change your password',
CHANGE_LATER: 'You can change your password in 5 minutes!',
INVALID_REFRESH_TOKEN: 'Your session has expired!',
EXPIRED_SESSION : 'Your session has expired!',
INVALID_USER_TYPE: 'Users with other roles than "customer" must use this url for login: ' + API_SERVER_BASE,
INVALID_CHANGE_TOKEN: 'Invalid change token value!',
PASSWORDS_MISSING: 'Password can not be empty',
WRONG_USERNAME: 'Invalid username!',
PASSWORD_GENERATED: 'Password has been changed!',
ERROR_PASSWORD_GENERATED: 'There seems to be a problem and the password was not saved!',
PASSWORD_SAME: 'The password cannot be the same with the old password!',
PASSWORD_MISMATCH: 'The new password and the confirmation password are not the same!',
PASSWORD_INCORRECT: 'The password must contain at least one lower case letter, one uppercaser letter, one number and one special character!',
FORGOT_REQUEST_SENT: 'Sending request...'
}
export const loginTexts = {
labels: {
CHANGE_PASSWORD: 'Change Password',
NEW_PASSWORD: 'New password',
CONFIRM_PASSWORD: 'Confirm password',
WELCOME_TO: 'Welcome to',
SIGN_IN: 'Sign In',
USERNAME: 'Username',
PASSWORD: 'Password',
},
buttons: {
CHANGE_PASSWORD: 'Change Password',
SIGN_IN: 'Sign In',
BACK_TO_SIGNIN: 'Back to Sign In',
FORGOT_PASSWORD: 'Forgot password?'
}
};

View File

@@ -0,0 +1,11 @@
const MODULE = 'BIDS_';
export const REQUEST_SET_BID = MODULE + 'REQUEST_SET_BID';
export const bidsTexts = {
messages: {
BID_SET: 'Bid set!',
CART_REQUIRED: 'Invalid cart item!',
BID_NOT_USED: 'There seems to be a problem and the bid was not set!'
}
}

View File

@@ -0,0 +1,176 @@
import CartUploadDocumentsContainer from '../containers/cart/CartUploadDocumentsContainer.jsx';
import CartCustomerDetailsContainer from '../containers/cart/CartCustomerDetailsContainer.jsx';
import CartReviewOrderContainer from '../containers/cart/CartReviewOrderContainer.jsx';
const MODULE = 'CART_';
export const REQUEST_SHOP_CART_COUNT = MODULE + 'REQUEST_SHOP_CART_COUNT';
export const RECEIVE_SHOP_CART_COUNT = MODULE + 'RECEIVE_SHOP_CART_COUNT';
export const REQUEST_SHOP_CART_ITEMS = MODULE + 'REQUEST_SHOP_CART_ITEMS';
export const RECEIVE_SHOP_CART_ITEMS = MODULE + 'RECEIVE_SHOP_CART_ITEMS';
export const REQUEST_CUSTOMER_DETAILS = MODULE + 'REQUEST_CUSTOMER_DETAILS';
export const RECEIVE_CUSTOMER_DETAILS = MODULE + 'RECEIVE_CUSTOMER_DETAILS';
export const SET_CURRENT_STEP = MODULE + 'SET_CURRENT_STEP';
export const RESET_STEPS = MODULE + 'RESET_STEPS';
export const GO_TO_NEXT_STEP = MODULE + 'GO_TO_NEXT_STEP';
export const GO_TO_PREVIOUS_STEP = MODULE + 'GO_TO_PREVIOUS_STEP';
export const LOAD_CART_STEPS = MODULE + 'LOAD_CART_STEPS';
export const UPLOAD_DOCUMENT = MODULE + 'UPLOAD_DOCUMENT';
export const REQUEST_CART_DOCUMENTS = MODULE + 'REQUEST_CART_DOCUMENTS';
export const RECEIVE_CART_DOCUMENTS = MODULE + 'RECEIVE_CART_DOCUMENTS';
export const SELECT_COUNTRY_DELIVERY = MODULE + 'SELECT_COUNTRY_DELIVERY';
export const SELECT_COUNTRY_BILLING = MODULE + 'SELECT_COUNTRY_BILLING';
export const REVIEW_ORDER = MODULE + 'REVIEW_ORDER';
export const SET_ORDER_INFO = MODULE + 'SET_ORDER_INFO';
export const IS_CART_ITEMS_DISABLED = MODULE + 'IS_CART_ITEMS_DISABLED';
export const RECEIVE_ORDER_TOTAL_PRICE = MODULE + 'RECEIVE_ORDER_TOTAL_PRICE';
export const UPDATE_CART_ITEMS = MODULE + 'UPDATE_CART_ITEMS';
export const SET_NEXT_STEP = MODULE + 'SET_NEXT_STEP';
export const SET_PREV_STEP = MODULE + 'SET_PREV_STEP';
export const SET_ORDER_PLACED = MODULE + 'SET_ORDER_PLACED';
export const SET_ORDER_PLACED_REDIRECT = MODULE + 'SET_ORDER_PLACED_REDIRECT';
export const cartSteps = () => ({
cartUploadDocuments: {
container: CartUploadDocumentsContainer,
name: 'Upload documents',
next: 'cartCustomerDetails',
previous: '',
status: 'active'
},
cartCustomerDetails: {
container: CartCustomerDetailsContainer,
name: 'Delivery and billing details',
next: 'cartReviewOrder',
previous: 'cartUploadDocuments',
status: 'inactive'
},
cartReviewOrder: {
container: CartReviewOrderContainer,
name: 'Review and confirm',
next: '',
previous: 'cartCustomerDetails',
status: 'inactive'
}
});
export const cartMessages = {
NOT_AVAILABLE: "This package is no longer available!",
NO_PACKAGES: "For the moment there are no packages available for your selection!",
INVALID_SELECTION: "Invalid selection!",
INVALID_USER: "This package is not available for this user!",
ONLY_ONE_CL: "You already have products from another reseller in the cart! One order can contain packages from a single reseller!",
PACKAGE_ALREADY_IN_CART: "This package is already in the cart!",
PACKAGE_ADDED: "Package added to cart!",
OPTIONS_ADDED: "Package options saved!",
QUANTITY_UPDATED: "Quantity has been updated for the package!",
INVALID_PACKAGE_FOR_REMOVE: "This package is not available in your cart! In case this error persists contact Co-Market!",
PACKAGE_REMOVED_FROM_CART: "The package has been removed from the cart!",
INVALID_QUANTITY: "The quantity can be a number between 1 and 100!",
CART_EMPTY: "You must have at least one package in the cart!",
NO_DELIVERY_ADDRESS: "No delivery address was added! Please fill the necessary info",
INCOMPLETE_DELIVERY_ADDRESS: "Incomplete delivery address. Please review!",
INCOMPLETE_BILLING_ADDRESS: "Incomplete billing address. Please review!",
INVALID_LENGTH_DELIVERY_ADDRESS: "Delivery address must not exceed 500 characters",
INVALID_LENGTH_BILLING_ADDRESS: "Billing detailed address must not exceed 500 characters",
INVALID_LENGTH_COMPANY_NAME: "Company name is too long. Maximum 300 characters!",
INVALID_LENGTH_FIRST_NAME: "First name is too long. Maximum 200 characters!",
INVALID_LENGTH_LAST_NAME: "Last name is too long. Maximum 200 characters!",
INVALID_LENGTH_CITY: "City name is too long. Maximum 100 characters",
INVALID_LENGTH_ZIP: "Zip code is too long. Maximum 20 characters",
LINK_CUSTOMER_CL: "There is no link between you and the reseller!",
ORDER_PLACED: "Order was successfully placed!",
ORDER_ERROR: "Something went wrong when placing the order!",
NO_PACKAGES_IN_CART: "Go to Co-Market to add packages.",
BROKER_MAIL_SENT: "A confirmation email was sent to Co-Market!",
MAIL_SENT: "Confirmation mail was sent with the order details!",
ERROR_MAIL_SENT: "Error when sending mail!",
ERROR_ORDER_INSERT: "An error occured while placing the order!",
ERROR_ORDER_PACK_RELATION: "An error occured while linking the packages to the order!",
ERROR_ON_ADDING_DOCUMENTS: "An error occured while linking the required documents",
ERROR_ORDER_DELIVERY_ADDRESS: "An error occured when adding the delivery address!",
ERROR_ORDER_BILLING_ADDRESS: "An error occured when adding the billing address!",
ADDRESS_INSERTED: "Delivery and billing addresses were saved!",
PACKAGES_DELETED: "Your cart is now empty!",
NOT_UPLOADED: "You need to upload all questionaires to go further!",
NO_FILE: "File is not valid! Supported formats are .docx, .doc, .xlsx, .xls, .odt, .ods, .pdf, .png, .jpg, .jpeg",
UPLOAD_ERROR: "There seems to be an error and the file is not uploaded",
FILE_UPLOADED: "Questionaire has been uploaded!",
NOT_LINKED_TO_CART: "Questionaire has not been linked and needs to be uploaded again!",
NO_SUPPORT: "File Drag/Drop is not supported for this browser",
NOT_ACCEPTED_TERMS: "You have to accept the terms and conditions for the order to be placed.",
PACKAGE_UNAVIALABLE: "Package is no longer available!",
CL_UNAVIALABLE: "This reseller is no longer available!",
PRODUCT_NOT_AVAILABLE: "Please remove packages that are no longer available!",
MAX_CHARACTERS: "Too many characters!",
QUESIONNAIRE_NOT_REQUIRED: "This packages do not require a questionnaire to be filed!",
AGREEMENT_NOT_REQUIRED: "This packages do not require an agreement to be filed!",
EXTRA_NOT_AVAILABLE: "This selection is not available for the selected price type!",
UNAVAILABLE_PACKAGES: "Unavailable packages:",
OPTIONS_UNAVAILABLE: "Some options are no longer available!",
ADDITIONAL_PACKAGES_UNAVAILABLE: "Some additional packages are no longer available",
INSTALLATION_SAVED: "Installation company was saved.",
INFO_INCOMPLETE: "Complete all the information for delivery and billing to continue!",
DOCS_MISSING: "Upload all required documents before proceding!",
NO_CHANGE: "No change has been found",
INVALID_FILE : 'Invalid file format! Supported extensions: .pdf, .docx, .doc, .xlsx, .xls, .odt, .ods',
UNAVAILABLE_PACKAGE_IN_CART: 'This package contains unavailable products. Remove from the cart to continue: ',
PRICE_DIGITS_TOO_MANY: 'Price should have no more than 15 digits'
}
export const cartTexts = {
labels: {
ON_DELIVERY: 'On delivery',
MONTHLY: 'Monthly',
EMPTY_CART: 'Your cart is empty. You can add packages from Co-Market.',
VAT: 'VAT code',
REFERENCE: 'Location details',
BID: 'Invoice reference',
DELIVERY_ADDRESS: 'Delivery address',
ADDRESS: 'Address',
CITY: 'City',
ZIP: 'Zip code',
COUNTRY: 'Country name',
BILLING_ADDRESS: 'Billing details',
COMPANY: 'Company name',
FIRST_NAME: 'First name',
LAST_NAME: 'Last name',
SAME_BILLING: 'Use same billing address as for delivery',
TOTAL_PRICE: 'Total price',
CONFIRM: 'Confirm order',
CONFIRM_TEXT: 'Are you sure you want to place the order?',
ITEMS_IN_CART_ICON: 'items in cart',
EMPTY_CART_ICON: 'Cart is empty',
REMOVE_ITEM_HEADER: 'Remove item confirmation',
REMOVE_ITEM_TEXT: 'Are you sure you want to remove ',
REMOVE_FROM_CART: 'Remove from cart',
ADDITIOONAL_PACKAGE: 'Additional package',
DOC_NOT_REQUIRED: 'Document not required',
FILE_UPLOADED_TEXT: 'File uploaded! Select or drop to replace ',
FILE: 'file',
NO_FILE_UPLOAD_TEXT_1: 'Click here to select',
NO_FILE_UPLOAD_TEXT_2: 'or drag and drop file here',
TEMPLATES: 'Templates',
UPLOADED: 'Uploaded dcuments',
PACKAGE_UNAVAILABLE: 'This package is no longer available. Remove it from the cart to place the order successfully',
BID_AVAILABLE: 'Bids available!',
AVAILABLE_BIDS: 'Available Bids',
BID_NUMBER: 'Bid Number',
START_DATE: 'Start Date',
END_DATE: 'End Date',
FIXED: 'Fixed',
RECURRENT: 'Recurrent',
SERVICES: 'Services',
ACTIONS: 'Actions',
PROJECT: 'Project',
INVOICE_MAIL: 'Invoice mail'
},
buttons: {
YES: 'Yes',
CANCEL: 'Cancel',
PREVIOUS: 'Previous',
NEXT: 'Next',
ADD_ADDRESS: 'Add address',
USE: 'Use',
REMOVE: 'Remove'
}
};

View File

@@ -0,0 +1,110 @@
const MODULE = 'CO_MARKET_';
export const REQUEST_SHOP_PACKAGES = MODULE + 'REQUEST_SHOP_PACKAGES';
export const RECIEVE_SHOP_PACKAGES = MODULE + 'RECIEVE_SHOP_PACKAGES';
export const REQUEST_SHOP_COMMERCIAL_LEADS = MODULE + 'REQUEST_SHOP_COMMERCIAL_LEADS';
export const RECIEVE_SHOP_COMMERCIAL_LEADS = MODULE + 'RECIEVE_SHOP_COMMERCIAL_LEADS';
export const SELECT_SHOP_COMMERCIAL_LEAD = MODULE + 'SELECT_SHOP_COMMERCIAL_LEAD';
export const REQUEST_PACKAGE_DETAILS = MODULE + 'REQUEST_PACKAGE_DETAILS';
export const RECIEVE_PACKAGE_DETAILS = MODULE + 'RECIEVE_PACKAGE_DETAILS';
export const SELECT_OPTION = MODULE + 'SELECT_OPTION';
export const SELECT_ADDITIONAL = MODULE + 'SELECT_ADDITIONAL';
export const REMOVE_ADDITIONAL = MODULE + 'REMOVE_ADDITIONAL';
export const SELECT_AGREEMENT = MODULE + 'SELECT_AGREEMENT';
export const CLEAR_SELECTIONS = MODULE + 'CLEAR_SELECTIONS';
export const REQUEST_ADD_TO_CART = MODULE + 'REQUEST_ADD_TO_CART';
export const SET_PACKAGE_FROM_URL = MODULE + 'SET_PACKAGE_FROM_URL';
export const coMarketMessages = {
NOT_AVAILABLE: "This package is no longer available!",
NO_PACKAGES: "For the moment there are no packages available for your selection!",
INVALID_SELECTION: "Invalid selection!",
INVALID_USER: "This package is not available for this user!",
ONLY_ONE_CL: "You already have products from another reseller in the cart! One order can contain packages from a single reseller!",
ONLY_ONE_COUNTRY: "You already have products from another country in the cart! One order can contain packages from a single country!",
PACKAGE_ALREADY_IN_CART: "This package is already in the cart!",
PACKAGE_ADDED: "Package added to cart!",
OPTIONS_ADDED: "Package options saved!",
QUANTITY_UPDATED: "Quantity has been updated for the package!",
INVALID_PACKAGE_FOR_REMOVE: "This package is not available in your cart! In case this error persists contact the Co-Market!",
PACKAGE_REMOVED_FROM_CART: "The package has been removed from the cart!",
INVALID_QUANTITY: "The quantity can be a number between 1 and 100!",
CART_EMPTY: "You must have at least one package in the cart!",
NO_DELIVERY_ADDRESS: "No delivery address was added! Please fill the necessary info",
INCOMPLETE_DELIVERY_ADDRESS: "Incomplete delivery address. Please review!",
INCOMPLETE_BILLING_ADDRESS: "Incomplete billing address. Please review!",
INVALID_LENGTH_DELIVERY_ADDRESS: "Delivery detailed address should be not bigger than 500 characters",
INVALID_LENGTH_BILLING_ADDRESS: "Billing detailed address should be not bigger than 500 characters",
INVALID_LENGTH_COMPANY_NAME: "Company name is too long. Maximum 300 characters!",
INVALID_LENGTH_FIRST_NAME: "First name is too long. Maximum 200 characters!",
INVALID_LENGTH_LAST_NAME: "Last name is too long. Maximum 200 characters!",
INVALID_LENGTH_CITY: "City name is too long. Maximum 100 characters",
INVALID_LENGTH_ZIP: "Zip code is too long. Maximum 20 characters",
LINK_CUSTOMER_CL: "There is no link between you and the reseller!",
ORDER_PLACED: "Order was successfully placed!",
ORDER_ERROR: "Something went wrong when placing the order!",
NO_PACKAGES_IN_CART: "Go to the Co-Market to add packages.",
BROKER_MAIL_SENT: "A confirmation email was sent to the Co-Market!",
MAIL_SENT: "Confirmation mail was sent with the order details!",
ERROR_MAIL_SENT: "Error when sending mail!",
ERROR_ORDER_INSERT: "An error occured while placing the order!",
ERROR_ORDER_PACK_RELATION: "An error occured while linking the packages to the order!",
ERROR_ON_ADDING_DOCUMENTS: "An error occured while linking the required documents",
ERROR_ORDER_DELIVERY_ADDRESS: "An error occured when adding the delivery address!",
ERROR_ORDER_BILLING_ADDRESS: "An error occured when adding the billing address!",
ADDRESS_INSERTED: "Delivery and billing addresses were saved!",
PACKAGES_DELETED: "Your cart is now empty!",
NOT_UPLOADED: "You need to upload all questionaires to go further!",
NO_FILE: "File is not valid! Supported formats are .docx, .doc, .xlsx, .xls, .odt, .ods, .pdf, .png, .jpg, .jpeg",
UPLOAD_ERROR: "There seems to be an error and the file is not uploaded",
FILE_UPLOADED: "Questionaire has been uploaded!",
NOT_LINKED_TO_CART: "Questionaire has not been linked and needs to be uploaded again!",
NO_SUPPORT: "File Drag/Drop is not supported for this browser",
NOT_ACCEPTED_TERMS: "You have to accept the terms and conditions for the order to be placed.",
PACKAGE_UNAVIALABLE: "Package is no longer available!",
CL_UNAVIALABLE: "This reseller is no longer available!",
PRODUCT_NOT_AVAILABLE: "Please remove packages that are no longer available!",
MAX_CHARACTERS: "Too many characters!",
QUESIONNAIRE_NOT_REQUIRED: "This packages do not require a questionnaire to be filed!",
AGREEMENT_NOT_REQUIRED: "This packages do not require an agreement to be filed!",
EXTRA_NOT_AVAILABLE: "This selection is not available for the selected price type!",
UNAVAILABLE_PACKAGES: "Unavailable packages:",
OPTIONS_UNAVAILABLE: "Some options are no longer available!",
ADDITIONAL_PACKAGES_UNAVAILABLE: "Some additional packages are no longer available",
INSTALLATION_SAVED: "Installation company was saved."
};
export const coMarketTexts = {
labels : {
ON_DELIVERY: 'On delivery',
MONTHLY: 'Monthly',
NOT_AVAILABLE: 'Not available',
SELECTION_NOT_AVAILABLE: 'This selection is not available for the selected price type!',
RECURRENT_PRICE: 'Package recurent price',
SERVICE_PRICE: 'Services and support',
EXTEND: 'with possibility to extend each',
PACKAGE_OPTIONS: ' ',
ADDITIONAL_PACKAGES: 'Add-ons',
AGREEMENT_OPTIONS: 'Payment options',
CATALOGUE: 'Catalogue',
SELECT_CL: 'Select reseller...',
AVAILABLE_IN: 'Available in',
DOCUMENTS: 'Documents',
NEW_PRODUCTS: 'New Products',
SEARCH_PACKAGE: 'search package...',
COMMERCIAL_HEADER_1: 'Next-generation Collaboration',
COMMERCIAL_HEADER_2: `Ricoh is now launching Huddle Concept - a stylishly designed,
multifunctional workspace that integrates video conferencing
with interactive whiteboard and enables more creative and interactive meetings`,
BY : 'by'
},
buttons: {
ADD_TO_CART: 'Add to cart',
DETAILS: 'Details'
}
}

View File

@@ -0,0 +1,44 @@
const MODULE = 'DASHBOARD_';
export const REQUEST_GADGETS = MODULE + 'REQUEST_GADGETS';
export const RECIEVE_GADGETS = MODULE + 'RECIEVE_GADGETS';
export const REQUEST_ORDERS = MODULE + 'REQUEST_ORDERS';
export const RECEIVE_ORDERS = MODULE + 'RECEIVE_ORDERS';
export const REQUEST_NEXT_ACTIONS = MODULE + 'REQUEST_NEXT_ACTIONS';
export const RECIEVE_NEXT_ACTIONS = MODULE + 'RECIEVE_NEXT_ACTIONS';
export const dashboardTexts = {
labels : {
ORDER_CENTRAL: 'Order Central',
NEXT_ACTIONS: 'Next Actions',
NO_ORDERS: 'For the moment you don\'t have any orders. Go to the Co-Market to add packages.',
NO_ACTIONS: 'No actions available.'
},
tableHeaders: {
ORDER: 'Order Number',
ORDER_DATE: 'Order Date',
PLACED_BY: 'Placed By',
REFERENCE: 'Location Details',
STATUS: 'Status',
ON_DELIVERY: 'On Delivery',
MONTHLY: 'Monthly'
},
buttons: {
DETAILS: 'Details'
},
statuses: {
open: 'Open',
'in-progress': 'In Progress',
production: 'Production',
'end-of-life': 'End Of Life',
canceled: 'Canceled',
'not-accepted': 'Not Accepted',
invalid: 'Invalid',
pending: 'Pending'
},
messages: {
ORDER_PLACED: 'Thank you for your order! You will be able to see the progress of it in the Details section of your order'
}
}

View File

@@ -0,0 +1,4 @@
const MODULE = 'DIALOG_';
export const CONTENT_MESSAGE = MODULE + 'CONTENT_MESSAGE';
export const IS_OPENED = MODULE + 'IS_OPENED';

View File

@@ -0,0 +1,6 @@
export const menuTexts = {
buttons : {
Profile: 'Profile',
Logout: 'Logout'
}
};

View File

@@ -0,0 +1,8 @@
const MODULE = 'NOTIFICATION_';
export const UPDATE_MESSAGES = MODULE + 'UPDATE_MESSAGES';
export const DESTROY_MESSAGE = MODULE + 'DESTROY_MESSAGE';
export const notificationMessages = {
HTML_ERROR : 'There seems to be a problem and the data could not be retrieved!'
};

View File

@@ -0,0 +1,15 @@
const MODULE = 'ORDER_PROJECTS_';
export const REQUEST_ORDER_PROJECTS = MODULE + 'REQUEST_ORDER_PROJECTS';
export const RECEIVE_ORDER_PROJECTS = MODULE + 'RECEIVE_ORDER_PROJECTS';
export const REQUEST_ADD_PROJECT = MODULE + 'REQUEST_ADD_PROJECT';
export const orderProjectsTexts = {
messages : {
'INVALID_DATA' : 'Invalid data!',
'PROJECT_ADDED' : 'Project added!',
'EMPTY_VALUE' : 'The field can not be empty:',
'MAX_CHARACTERS' : 'The field can not be longer than 100 characters:'
}
}

View File

@@ -0,0 +1,209 @@
const MODULE = 'ORDERS_';
export const REQUEST_ORDERS = MODULE + 'REQUEST_ORDERS';
export const RECEIVE_ORDERS = MODULE + 'RECEIVE_ORDERS';
export const REQUEST_ACTIVE_ORDERS = MODULE + 'REQUEST_ACTIVE_ORDERS';
export const RECEIVE_ACTIVE_ORDERS = MODULE + 'RECEIVE_ACTIVE_ORDERS';
export const REQUEST_HISTORY_ORDERS = MODULE + 'REQUEST_HISTORY_ORDERS';
export const RECEIVE_HISTORY_ORDERS = MODULE + 'RECEIVE_HISTORY_ORDERS';
export const REQUEST_ORDER_INFO = MODULE + 'REQUEST_ORDER_INFO';
export const RECEIVE_ORDER_INFO = MODULE + 'RECEIVE_ORDER_INFO';
export const SEND_ORDER_COMMENT = MODULE + 'SEND_ORDER_COMMENT';
export const REQUEST_CUSTOMER_DOCUMENTS = MODULE + 'REQUEST_CUSTOMER_DOCUMENTS';
export const RECEIVE_CUSTOMER_DOCUMENTS = MODULE + 'RECEIVE_CUSTOMER_DOCUMENTS';
export const RE_UPLOAD_DOCUMENT = MODULE + 'RE_UPLOAD_DOCUMENT';
export const REQUEST_VALIDATION_COMMENTS = MODULE + 'REQUEST_VALIDATION_COMMENTS';
export const RECEIVE_VALIDATION_COMMENTS = MODULE + 'RECEIVE_VALIDATION_COMMENTS';
export const REQUEST_CUSTOMER_ACCEPTANCE = MODULE + 'REQUEST_CUSTOMER_ACCEPTANCE';
export const RECEIVE_CUSTOMER_ACCEPTANCE = MODULE + 'RECEIVE_CUSTOMER_ACCEPTANCE';
export const UPLOAD_CUSTOMER_ACCEPTANCE = MODULE + 'UPLOAD_CUSTOMER_ACCEPTANCE';
export const SEND_CUSTOMER_ACCEPTANCE = MODULE + 'SEND_CUSTOMER_ACCEPTANCE';
export const REQUEST_IS_NEXT_STEP_WANTED = MODULE + 'REQUEST_IS_NEXT_STEP_WANTED';
export const RECEIVE_IS_NEXT_STEP_WANTED = MODULE + 'RECEIVE_IS_NEXT_STEP_WANTED';
export const RECEIVE_IS_COMPONENT_DISABLED = MODULE + 'RECEIVE_IS_COMPONENT_DISABLED';
export const SET_EARLIEST_INSTALLATION_DATE = MODULE + 'SET_EARLIEST_INSTALLATION_DATE';
export const SET_CONFIRMATION_DATES = MODULE + 'SET_CONFIRMATION_DATES';
export const REQUEST_INSTALLATION_COMPANIES = MODULE + 'REQUEST_INSTALLATION_COMPANIES';
export const RECEIVE_INSTALLATION_COMPANIES = MODULE + 'RECEIVE_INSTALLATION_COMPANIES';
export const SET_SUPPORT_MESSAGE = MODULE + 'SET_SUPPORT_MESSAGE';
export const SET_SCHEDULING_DISABLED_FLAG = MODULE + 'SET_SCHEDULING_DISABLED_FLAG';
export const SET_VIEW_ALL_ORDERS = MODULE + 'SET_VIEW_ALL_ORDERS';
export const REQUEST_ALL_SHIPPING_DATES_CONFIRMED = MODULE + 'REQUEST_ALL_SHIPPING_DATES_CONFIRMED';
export const RECEIVE_ALL_SHIPPING_DATES_CONFIRMED = MODULE + 'RECEIVE_ALL_SHIPPING_DATES_CONFIRMED';
export const orderMessages = {
SYSTEM_ALLOWED_LANGUAGES_EMPTY: 'There are no languages added in the system.',
ALLOWED_LANGUAGE: 'Allowed languages are:',
ALLOWED_LANGUAGE_ERROR: 'There was an error while trying to detect the language:',
SERVER_ERROR: 'There seems to be a problem and the comment was not added!',
ORDER_COMMENT_ADDED: 'Order comment updated!',
FILE_UPLOADED : 'File has been uploaded and needs to be validated!',
NOT_UPLOADED : 'There seems to be a problem and the file was not uploaded!',
RE_UPLOAD_MAIL : 'Notify mail has been sent!',
ACCEPTANCE_NOT_UPDATED : 'Acceptance status not updated!',
ACCEPTANCE_NOT_UPLOADED: 'You need to uploade the acceptance document before you can agree with the installation!',
DECLINE_REASON_EMPTY : 'Please describe what is not to your satisfaction.',
INSTALLATION_DECLINED : 'The implementation is not satisfactory.',
ERROR_MAIL_SENT : 'Notify mail was not sent!',
ORDER_UPDATE_MAIL_SENT: 'Order update email was successfully sent',
INSTALLATION_DATE_EMPTY: 'Installation date cannot be empty.',
INVALID_DATE_ESTIMATED: 'Estimated date is in incorrect format.',
STATUS_NOT_SET: 'Status of the installation date is not set.',
EID_NOT_SET: 'Earliest installation date is not set.',
RELATION_NOT_FOUND: 'Relation between the order and installation company has not been set.',
PAST_DATE: 'Installation date cannot be earlier than',
PROPOSED_DATE_EXISTS: 'This date is already proposed',
MAX_SLA_EXCEEDED: 'Maximum installation date regarding SLA is excedeed.',
MAX_CHARACTERS: 'Too many characters for field: ',
INSTALLATION_ALREADY_ACCEPTED: 'The accepted installation date has been changed.',
INSTALLATION_DATE_UPDATED: 'Installation date was successfully updated.',
INSTALLATION_DATE_NOT_REMOVED: 'This installation date cannot be removed. Please refresh the page.',
INSTALLATION_DATE_REMOVED : 'My proposed installation date was successfully removed',
INSTALLATION_DATE_REMOVE_ERROR : 'There was an error while trying to remove my installation date proposed',
INSTALLATION_MAIL_SENT: 'Notification mail was sent',
INVALID_FILE_QUESTIONNAINRE : 'Invalid file format! Supported extensions: .pdf, .docx, .doc, .xlsx, .xls, .odt, .ods',
INVALID_FILE_ACCEPTANCE : 'Invalid file format! Supported extensions: .pdf, .docx, .doc, .xlsx, .xls, .odt, .ods, .jpg, .png, .jpeg',
EMPTY_VALUE: 'Field required: ',
INSTALLATION_ACCEPTED: 'The implementation has been accepted!',
ID_ORDER_NOT_SET: 'The id of the order is not set',
SUPPORT_MAIL_SENT: 'Support mail sent!'
};
export const orderTexts = {
labels: {
NOT_ACCEPTED: 'Customer acceptance must be submitted no later than : ',
ACCEPTED: 'Implementation accepted',
DECLINED: 'Implementation not satisfactory',
NOT_SET: 'not set',
REASON: 'Reason',
UPLOAD_ACCEPTANCE_LABEL: 'Select or drop acceptance file',
NO_DOCUMENTS_UPLOADED: 'No documents uploaded!',
ACCEPTANCE_LABEL: 'The system is implemented in a professional way and provides the agreed functionality.',
ACCEPT_INSTALLATION_TEXT: 'We are pleased to hear you are satisfied. Please continue by pressing the button below.',
DECLINE_REASON_TEXT: 'We are sorry to hear you are not satisfied with the implementation. Please describe why below, to help us get better.',
REFERENCE: 'Location Details',
LOCATION_DETAILS: 'Location details',
BID: 'Invoice reference',
ORDER_DATE: 'Order date',
EST_DELIVERY: 'Est. delivery date',
AMOUNT: 'Amount',
STATUS: 'Status',
SOLD_BY: 'Sold By',
TOTAL_DELVIERY_PRICE: 'On Delivery',
TOTAL_RECURRENT_PRICE: 'Monthly',
ACCEPTANCE_HEADER: 'Installation acceptance',
DELIVERY_DATE: 'Delivery date',
INSTALLATION_SCHEDULE_HEADER: 'Installation scheduling',
SUPPORT_MESSAGE_HEADER: 'Contact support',
TERMS_AND_COND: 'Terms and conditions',
DELIVERY_ADDRESS: 'Delivery address',
PHONE_NUMBER: 'Phone number',
MAIL: 'Mail address',
EXTEND: 'with possibility to extend each',
PACKAGE_PRICE: 'Package price',
RECURRENT_PRICE: 'Package recurent price',
SERVICES_PRICE: 'Services and support',
OPTIONS: 'Options',
ADDITIONAL_PACKAGES: 'Additional Packages',
DOCUMENTS: 'Documents',
NO_DOCUMENTS: 'No documents available',
OTHER_DOCS: 'Other documents',
PACKAGES: 'Packages',
COMPLETED: 'Completed',
WILL_BE_PROCESS: 'This order will be processed soon',
INSTALL_COMPANY: 'Installation company',
INSTALL_COMPANY_NOT_SET: 'Installation company has not yet been chosen by the broker',
EARLIEST_INSTALLATION: 'Earliest installation date',
PROPOSED_DATES: 'Proposed dates',
INSTALLATION_PROTOCOL: 'Installation protocol',
NO_PROTOCOL_UPLOADED: 'No documents uploaded yet',
INSTALLATION_MAIL_SENT: 'Notification mail was sent',
ERROR_MAIL_SENT: 'An error occured while trying to send a mail.',
ORDER_DETAILS: 'Order details',
ORDER_NUMBER: 'Order number',
COMMERCIAL_LEAD: 'Catalogue',
ORDER_ITEMS: 'Order items',
PRICE: 'Price',
SERVICES_AND_SUPPORT: 'Services and support',
END_OF_LIFE: 'End of life',
ENTER_TEXT: 'Enter your text here',
SELECT_OR_DROP: 'Select or drop questionnaire',
ACTIVE_ORDERS: ' Active Orders',
ORDER_HISTORY: 'Orders History',
NO_RECORDS: 'No records available',
NOT_AVAILABLE: 'This order is not available',
ORDER: 'Order',
ORDER_INFO: 'Order Info',
COMMENTS: 'Comments',
SCHEDULE_INSTALLATION_ENABLED: 'Function to schedule the installation date',
SCHEDULE_INSTALLATION_DISABLED: 'Scheduling will be available when the delivery date is set by supplier',
SCHEDULE_INSTALLATION_NOT_EXIST: 'Installation is not required for this order',
PRELIMINARY_INSTALLATION_DATE: 'The date is preliminary until all shipping dates are confirmed',
INSTALLATION_DATE_ACCEPTED: 'The installation date is the final and will be performed at the accepted date',
INSTALLATION_DATE_NOT_SET: 'The installation date is not set yet',
PLACED_BY: 'Placed by',
VIEW_ALL_ORDERS: 'View all orders',
INSTALLATION_DATE: 'Installation date',
INSTALLATION_NOT_REQUIRED:'Installation is not required for this package',
PRELIMINARY_INSTALLATION_DATE_LABEL: 'Preliminary installation date',
PROJECT: 'Project'
},
buttons: {
ACCEPT_INSTALLATION: 'I agree',
DECLINE_INSTALLATION: 'I disagree',
DELIVERY_DETAILS: 'Delivery details',
DETAILS: 'Details',
SUPPORT: 'Support',
SEND: 'Send',
CANCEL: 'Cancel',
CLOSE: 'Close',
YES: 'Yes',
NO: 'No',
ACCEPT: 'Accept',
DECLINE: 'Decline',
REMOVE: 'Remove',
ADD_COMMENT: 'Add order comment',
SEND_MAIL_TO_SUPPORT: 'Send an email to support',
SEE_DOCUMENTS: 'See documents',
ALL: 'All',
SCHEDULE_INSTALLATION: 'Schedule installation',
ACTIONS: 'Actions'
},
statuses: {
open: 'Open',
'in-progress': 'In Progress',
production: 'Completed',
'end-of-life': 'Completed',
canceled: 'Canceled',
'not-accepted': 'Not Accepted',
invalid: 'Invalid',
pending: 'Pending'
},
headers: {
ORDER: 'Order',
REFERENCE: 'Location Details',
ORDER_DATE: 'Order date',
ESTIMATED_DATE: 'Est. delivery date',
COMMERCIAL_LEAD: 'Catalogue',
AMOUNT: 'Amount',
STATUS: 'Status',
DELIVERY_DATE: 'Delivery date',
END_OF_LIFE: 'End of Life',
PLACED_BY: 'Placed by'
}
};

View File

@@ -0,0 +1,8 @@
const MODULE = 'PAGE_';
export const SET_PARAMS_FROM_URL = MODULE + 'SET_PARAMS_FROM_URL';
export const RESET_PARAMS = MODULE + 'RESET_PARAMS';
export const SET_ACTIVE_MODULE = MODULE + 'SET_ACTIVE_MODULE';
export const SET_ACTIVE_SUB_MODULE = MODULE + 'SET_ACTIVE_SUB_MODULE';
export const RESET_ACTIVE_SUB_MODULE = MODULE + 'RESET_ACTIVE_SUB_MODULE';

View File

@@ -0,0 +1,82 @@
const MODULE = 'PROFILE_SETTINGS_';
export const REQUEST_PROFILE_INFO = MODULE + 'REQUEST_PROFILE_INFO';
export const RECIEVE_PROFILE_INFO = MODULE + 'RECIEVE_PROFILE_INFO';
export const REQUEST_SAVE_PROFILE = MODULE + 'REQUEST_SAVE_PROFILE';
export const REQUEST_SAVE_COMPANY = MODULE + 'REQUEST_SAVE_COMPANY';
export const REQUEST_SAVE_PROFILE_ADDRESS = MODULE + 'REQUEST_SAVE_PROFILE_ADDRESS';
export const REQUEST_REMOVE_PROFILE_ADDRESS = MODULE + 'REQUEST_REMOVE_PROFILE_ADDRESS';
export const REQUEST_COUNTRIES = MODULE + 'REQUEST_COUNTRIES';
export const RECIEVE_COUNTRIES = MODULE + 'RECIEVE_COUNTRIES';
export const REQUEST_REMOVE_BILLING_ADDRESS = MODULE + 'REQUEST_REMOVE_BILLING_ADDRESS';
export const REQUEST_SAVE_BILLING_ADDRESS = MODULE + 'REQUEST_SAVE_BILLING_ADDRESS';
export const profileTexts = {
labels: {
PROFILE_EDIT_TITLE: 'Edit profile data',
COMPANY_EDIT_TITLE: 'Edit company data',
PROFILE_NAME: 'Name',
PROFILE_PHONE: 'Phone',
COMPANY_NAME: 'Company Name',
VAT_CODE: 'VAT',
CHANGE_PASSWORD: 'Change password',
PROFILE_ADDRESSES: 'Delivery Addresses',
ADD_PROFILE_ADDRESS: 'Add delivery address',
EDIT_PROFILE_ADDRESS: 'Edit devliery address',
REMOVE_PROFILE_ADDRESS: 'Remove delivery address',
ADD_BILLING_ADDRESS: 'Add billing address',
EDIT_BILLING_ADDRESS: 'Edit billing address',
REMOVE_BILLING_ADDRESS: 'Remove billing address',
REMOVE_CONFIRMATION: 'Are you sure you want to remove this address?',
ADDRESS_COUNTRY: 'Country',
ADDRESS_CITY: 'City',
ADDRESS_DETAILS: 'Address',
ADDRESS_ZIP: 'Zip',
BILLING_ADDRESSES: 'Billing addresses',
FIRST_NAME: 'First Name',
LAST_NAME: 'Last Name',
DELEGATE: 'Attention',
INVOICE_MAIL: 'Invoice Mail'
},
buttons: {
SAVE: 'Save',
GENERATE_TOKEN: 'Send password change token by mail',
ADD_ADDRESS: 'Add address',
CANCEL: 'Cancel',
YES: 'Yes',
NO: 'No'
},
messages: {
INVALID_USER: 'You are not allowed to change the informations for this user!',
INVALID_PROFILE_DATA: 'The profile information provided is not valid!',
ADD_NAME: 'The name can not be empty!',
ADD_PHONE_NUMBER: 'The phone number can not be empty!',
INVALID_PHONE_NUMBER: 'Invalid phone number format!',
INVALID_USER_TYPE: 'Invalid user type!',
PROFILE_UPDATED: 'Profile info updated!',
PROFILE_NOT_CHANGED: 'No changes!',
NOT_COMPANY_ADMIN: 'You are not allowed to change the company information! Contact your company admin user.',
ADD_VAT: 'The vat code can not be empty!',
ADD_COPMANY_NAME: 'The company name can not be empty!',
COMPANY_UPDATED: 'Company info updated!',
COMPANY_NOT_CHANGED: 'No changes!',
ADD_COMPANY: 'Invalid company!',
NO_ADDRESS_SELECTED: 'No address selected!',
ADDRESS_REMOVED: 'Profile address removed!',
BILLING_ADDRESS_REMOVED: 'Billing address removed!',
ADDRESS_ERROR: 'There seems to be a problem and the address could not be removed!',
NOT_ADDRESS_OWNER: 'You are not allowed to modify this address!',
INVALID_PROFILE_ADDRESS: 'The address provided is not valid',
PROFILE_ADDRESS_UPDATED: 'Delivery address saved!',
PROFILE_ADDRESS_NOT_CHANGED: 'Delivery address not changed!',
BILLING_ADDRESS_UPDATED: 'Billing address saved!',
BILLING_ADDRESS_NOT_CHANGED: 'Billing address not changed!',
ADD_COUNTRY: 'The country field can not be empty',
ADD_CITY: 'The city field can not be empty',
ADD_ADDRESS: 'The address field can not be empty',
ADD_ZIP: 'The zip field can not be empty',
ADD_FIRST_NAME: 'The first name field can not be empty',
ADD_LAST_NAME: 'The last name field can not be empty',
ADD_INVOICE_MAIL: 'The invoice mail field can not be empty',
INVALID_INVOICE_MAIL: 'Invalid invoice mail address'
}
};

View File

@@ -0,0 +1,5 @@
export const termsTexts = {
labels: {
TERMS_AND_CONDITIONS: 'Terms and conditions'
}
};

View File

@@ -0,0 +1,47 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {BrowserRouter} from 'react-router-dom';
import {getModules} from '../actions/login/authActions';
import {setActiveModule} from '../actions/page/pageActions';
import Menu from '../mainComponents/menu/Menu.jsx';
import WiaasRouter from '../mainComponents/wiaasRouter/WiaasRouter.jsx';
import Footer from './footer/Footer.jsx';
import './contentContainer.css';
class ContentContainer extends Component {
constructor(props) {
super(props);
this.addActiveClass = this.addActiveClass.bind(this);
}
componentDidMount() {
this.props.dispatch(getModules());
}
addActiveClass(moduleName) {
this.props.dispatch(setActiveModule(moduleName));
}
render() {
const {activeModule} = this.props;
return (
<BrowserRouter>
<div className="main-wrapper">
<header className="App-header">
<Menu addActiveClass={this.addActiveClass} activeModule={activeModule} />
</header>
<WiaasRouter addActiveClass={this.addActiveClass} />
<Footer/>
</div>
</BrowserRouter>
);
}
}
const mapStateToProps = (state) => ({
activeModule: state.pageReducer.activeModule
});
export default connect(mapStateToProps)(ContentContainer);

View File

@@ -0,0 +1,44 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {Row, Col, Container, NavLink} from 'reactstrap';
import CartStepsContainer from './CartStepsContainer.jsx';
import CartItemsContainer from './CartItemsContainer.jsx';
import {fetchCartCount} from '../../actions/cart/cartActions';
import {cartTexts} from '../../constants/cartConstants';
class CartContainer extends Component {
componentDidMount() {
this.props.dispatch(fetchCartCount());
}
render() {
const {cartSteps, currentStep, cartCount} = this.props;
const TagName = currentStep && cartSteps && cartSteps[currentStep] ? cartSteps[currentStep].container : null;
return (
<Container fluid={true} id="cart-container">
{ cartCount
? <div>
<CartStepsContainer/>
<Row>
<Col xl="5" lg="5" md="12" xs="12"><CartItemsContainer /></Col>
<Col xl="7" lg="7" md="12" xs="12">{TagName && <TagName/>}</Col>
</Row>
</div>
: <Row>
<NavLink tag={Link} to='/co-market' id="go-to-co-market-text">{cartTexts.labels.EMPTY_CART}</NavLink>
</Row>
}
</Container>
);
}
}
const mapStateToProps = (state) => ({
cartSteps: state.cartReducer.cartSteps,
currentStep: state.cartReducer.currentStep,
cartCount: state.cartReducer.cartCount
});
export default connect(mapStateToProps)(CartContainer);

View File

@@ -0,0 +1,229 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Form, Input} from 'reactstrap';
import {WiaasTable, WiaasTableBody} from '../../mainComponents/table/WiaasTable.jsx';
import ProfileAddressesContainer from '../profileSettings/ProfileAddressesContainer.jsx';
import BillingAddressesContainer from '../profileSettings/BillingAddressesContainer.jsx';
import OrderProjectsSelect from './components/OrderProjectsSelect.jsx';
import {getCustomerDetails,setCartItemsDisabled, nextStep, previousStep, saveOrderDetails, fetchCartItems, setNextActionFct, setPrevActionFct} from '../../actions/cart/cartActions';
import {cartMessages} from '../../constants/cartConstants';
import {updateMessages} from '../../actions/notification/notificationActions';
import {fetchProfileInfo, fetchCountries} from '../../actions/profileSettings/profileSettingsActions';
import {cartTexts} from '../../constants/cartConstants';
import './style/Cart.css';
class CartCustomerDetailsContainer extends Component {
constructor(props) {
super(props);
this.state = {
vatCode: '',
companyName: '',
details: {
refernce: '',
tender: '',
idProject: ''
},
delivery: {},
billing: {},
project: {},
checked: false,
billingCountry: {}
};
this.handleDetailsChange = this.handleDetailsChange.bind(this);
this.handleDeliveryChange = this.handleDeliveryChange.bind(this);
this.handleBillingChange = this.handleBillingChange.bind(this);
this.handleProjectChange = this.handleProjectChange.bind(this);
this.setBillingAsDelivery = this.setBillingAsDelivery.bind(this);
this.checkIfInfoCompleted = this.checkIfInfoCompleted.bind(this);
this.handleNextAction = this.handleNextAction.bind(this);
this.handlePrevAction = this.handlePrevAction.bind(this);
}
componentDidMount() {
this.props.dispatch(getCustomerDetails());
this.props.dispatch(fetchCartItems());
this.props.dispatch(setNextActionFct(this.handleNextAction));
this.props.dispatch(setPrevActionFct(this.handlePrevAction));
this.props.dispatch(fetchProfileInfo(this.props.userInfo.wiaas_id_user));
this.props.dispatch(fetchCountries());
}
componentWillReceiveProps(nextProps) {
if(nextProps.customerDetails && nextProps.profileInfo) {
const selectedDeliveryAddress = nextProps.profileInfo.profileAddresses.find((address) => {
return address.id === nextProps.customerDetails.details.idDeliveryAddress
}) || nextProps.profileInfo.profileAddresses[0] || {};
const selectedBillingAddress = nextProps.profileInfo.billingAddresses.find((address) => {
return address.id === nextProps.customerDetails.details.idBillingAddress
}) || nextProps.profileInfo.billingAddresses[0] || {};
this.setState({
delivery: selectedDeliveryAddress,
billing: selectedBillingAddress,
vatCode: nextProps.profileInfo.vatCode,
companyName: nextProps.profileInfo.companyName,
details: nextProps.customerDetails.details
});
}
}
handleDetailsChange(event) {
const fieldName = event.target.name;
const details = this.state.details;
details[fieldName] = event.target.value;
return this.setState({details});
}
handleDeliveryChange(deliveryAddress) {
return this.setState({delivery: deliveryAddress});
}
handleBillingChange(billingDetails) {
return this.setState({billing: billingDetails});
}
handleProjectChange(project){
const newDetails = this.state.details;
newDetails.idProject = project ? project.idProject : null;
return this.setState({details: newDetails});
}
setBillingAsDelivery(e) {
this.setState({
checked: !this.state.checked
});
setTimeout(() => {
if (this.state.checked) {
var newBillingState = Object.assign({}, this.state.billing, this.state.delivery);
this.props.dispatch(saveOrderDetails({delivery: this.state.delivery, billing: newBillingState, details: this.state.details}, this.props.cartItems));
}
}, 10);
}
handleNextAction() {
if(this.checkIfInfoCompleted()) {
this.props.dispatch(nextStep());
this.props.dispatch(setCartItemsDisabled(true));
this.props.dispatch(saveOrderDetails(this.state, this.props.cartItems));
}
}
handlePrevAction() {
if(this.checkIfInfoCompleted()) {
this.props.dispatch(previousStep());
this.props.dispatch(setCartItemsDisabled(false));
this.props.dispatch(saveOrderDetails(this.state, this.props.cartItems));
}
}
checkIfInfoCompleted() {
const {delivery, billing} = this.state;
const isDeliveryAddressCompleted = Object.keys(delivery).length ? Object.keys(delivery).every(delivKey => {
return delivery[delivKey] !== '';
}) : false;
const isBillingAddressCompleted = Object.keys(billing).length ? Object.keys(billing).every(billingKey => {
return billing[billingKey] !== '' || billingKey === 'firstName' || billingKey === 'lastName' || billingKey === 'invoiceMail';
}) : false;
if(isDeliveryAddressCompleted && isBillingAddressCompleted) {
return true;
} else {
this.props.dispatch(updateMessages([
{
code: 'warning',
message: 'INFO_INCOMPLETE'
}
], cartMessages));
return false;
}
}
uncheckSameBillingIfChangesAreMade() {
if(this.state.checked) {
this.setState({ checked: !this.state.checked });
}
}
render() {
const {isLoading, profileInfo, isProfileLoading} = this.props;
return (
<div id="cart-customer-details-container">
<WiaasTable id="cart-customer-main-information-container">
<WiaasTableBody>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.VAT}</Col>
<Col md="4">{profileInfo && profileInfo.vatCode}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.COMPANY}</Col>
<Col md="4">{profileInfo && profileInfo.companyName}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.REFERENCE}</Col>
<Input className="col-md-4" id ="reference-nb" type="text" name="reference" value={this.state.details.reference || ''} onChange={this.handleDetailsChange} />
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.BID}</Col>
<Input className="col-md-4" id="tender-nb" type="text" name="tender" value={this.state.details.tender || ''} onChange={this.handleDetailsChange} />
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.PROJECT}</Col>
<OrderProjectsSelect handleChange={this.handleProjectChange} idProject={this.state.details.idProject}/>
</Row>
{
isLoading &&
<Row>
<Col className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</Col>
</Row>
}
{ !isLoading &&
<div>
<Form id="delivery-information" className="cart-address-box">
{
(profileInfo && !isProfileLoading) &&
<ProfileAddressesContainer
params={{location: 'cart', handleDeliveryChange: this.handleDeliveryChange, idSelectedDeliveryAddress: this.state.delivery.id}}
idUser={this.props.userInfo.wiaas_id_user}/>
}
</Form>
</div>
}
{ !isLoading &&
<div>
<Form id="billing-information" className="cart-address-box">
{
(profileInfo && !isProfileLoading) &&
<BillingAddressesContainer
idCompany={profileInfo.idCompany}
params={{location: 'cart', handleBillingChange: this.handleBillingChange, idSelectedBillingAddress: this.state.billing.id}}
idUser={this.props.userInfo.wiaas_id_user}/>
}
</Form>
</div>
}
</WiaasTableBody>
</WiaasTable>
</div>
);
}
}
const mapStateToProps = (state) => ({
cartItems: state.cartReducer.cartItems,
customerDetails: state.cartReducer.customerDetails,
isLoading: state.cartReducer.isLoading,
isProfileLoading: state.profileSettingsReducer.isLoading,
userInfo: state.auth.userInfo,
profileInfo: state.profileSettingsReducer.profileInfo,
});
export default connect(mapStateToProps)(CartCustomerDetailsContainer);

View File

@@ -0,0 +1,83 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col} from 'reactstrap';
import {WiaasTable, WiaasTableBody} from '../../mainComponents/table/WiaasTable.jsx';
import CartItem from './components/CartItem.jsx';
import {cartTexts} from '../../constants/cartConstants';
import './style/Cart.css';
class CartItemsContainer extends Component {
getOrderCommercialLead() {
return this.props.cartItems.length ? this.props.cartItems[0].commercialLead : '';
}
render() {
const {cartItems, isCartItemsDisabled, orderTotalPrice, isLoading} = this.props;
return (
<WiaasTable>
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(cartItems && cartItems.length && !isLoading) &&
<WiaasTableBody id="cart-items-container">
{cartItems.map((cartItem, mapKey) => <CartItem key={cartItem.idPackage}
cartItem={cartItem}
cartItemNo={mapKey}
isFormDisabled={isCartItemsDisabled}/>)}
<div id="total-price-in-cart-container" className="cart-show-items">
<Row className="cart-total-price">
<Col lg="5" xs="5" className="item-name">
{cartTexts.labels.TOTAL_PRICE}:
</Col>
{
orderTotalPrice &&
<Col id={"cart-total-price"}
lg="7" xs="7"
className="item-name">
<table className="price-table">
<thead>
<tr>
<th>{cartTexts.labels.ON_DELIVERY}</th>
<th>{cartTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{orderTotalPrice.fixedPrice.toLocaleString()} {orderTotalPrice.currency} {' '}
</td>
<td>
{
orderTotalPrice.recurrentPrice && orderTotalPrice.recurrentPrice.toLocaleString() + ' ' + orderTotalPrice.currency
}
</td>
</tr>
</tbody>
</table>
</Col>
}
</Row>
</div>
</WiaasTableBody>
}
</WiaasTable>
);
}
}
const mapStateToProps = (state) => ({
cartItems: state.cartReducer.cartItems,
isCartItemsDisabled: state.cartReducer.isCartItemsDisabled,
orderTotalPrice: state.cartReducer.orderTotalPrice,
isLoading: state.cartReducer.isLoading
});
export default connect(mapStateToProps)(CartItemsContainer);

View File

@@ -0,0 +1,177 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Redirect} from 'react-router-dom';
import {Row, Col, Button} from 'reactstrap';
import './style/Cart.css';
import {WiaasTable, WiaasTableBody} from '../../mainComponents/table/WiaasTable.jsx';
import {placeOrder, setCartItemsDisabled, previousStep, fetchCartItems, setPrevActionFct} from '../../actions/cart/cartActions';
import {setDialogContent, setDialogOpenFlag} from '../../actions/dialog/dialogActions';
import {cartTexts} from '../../constants/cartConstants';
class CartReviewOrderContainer extends Component {
constructor(props) {
super(props);
this.placeOrder = this.placeOrder.bind(this);
this.handlePreviousBtn = this.handlePreviousBtn.bind(this);
}
componentDidMount() {
this.props.dispatch(fetchCartItems());
this.props.dispatch(setPrevActionFct(this.handlePreviousBtn));
}
setDialogParams(dialogContent) {
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
placeOrder() {
this.props.dispatch(placeOrder(this.props.orderInfo));
}
handlePreviousBtn() {
this.props.dispatch(previousStep());
this.props.dispatch(setCartItemsDisabled(false));
}
getProjectName(idProject){
const selectedProject = this.props.orderProjects.find(orderProject => {
return orderProject.idProject === idProject
});
return selectedProject && selectedProject.projectName ? selectedProject.projectName : '-';
}
render() {
const {orderInfo, isLoading, orderPlaced, orderPlacedRedirect} = this.props;
const customerDetails = orderInfo && orderInfo.orderDetails;
const items = orderInfo && orderInfo.cartItems;
const dialogContent = {
buttons: [
{
color: 'success',
action: this.placeOrder,
name: cartTexts.buttons.YES,
id: 'place-order-confirmation'
}, {
color: 'secondary',
name: cartTexts.buttons.CANCEL,
id: 'cancel-remove-item'
}
],
header: cartTexts.labels.CONFIRM,
body: cartTexts.labels.CONFIRM_TEXT
};
return (
<div id="cart-review-all-order-container">
{ (customerDetails && items) &&
<WiaasTable>
<WiaasTableBody id="cart-review-order-container">
{ isLoading
? <Row>
<Col className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</Col>
</Row>
: <span>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.VAT}</Col>
<Col md="4" id="review-vatCode">{customerDetails.vatCode}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.REFERENCE}</Col>
<Col md="4" id="review-reference">{customerDetails.details.reference ? customerDetails.details.reference : '-'}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.BID}</Col>
<Col md="4" id="review-tender">{customerDetails.details.tender ? customerDetails.details.tender : '-'}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.PROJECT}</Col>
<Col md="4" id="review-tender">{customerDetails.details.idProject ? this.getProjectName(customerDetails.details.idProject) : '-'}</Col>
</Row>
<div>
<h4>{cartTexts.labels.DELIVERY_ADDRESS}</h4>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.ADDRESS}</Col>
<Col md="4"id="review-delivery-address">{customerDetails.delivery.detailedAddress}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.CITY}</Col>
<Col md="4" id="review-delivery-city">{customerDetails.delivery.city}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.ZIP}</Col>
<Col md="4" id="review-zip">{customerDetails.delivery.zipCode}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.COUNTRY}</Col>
<Col md="4" id="review-delivery-country">{customerDetails.delivery.countryName}</Col>
</Row>
</div>
<div>
<h4>{cartTexts.labels.BILLING_ADDRESS}</h4>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.COMPANY}</Col>
<Col md="4" id="review-billing-company">{customerDetails.companyName}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.FIRST_NAME}</Col>
<Col md="4" id="review-billing-first-name">{customerDetails.billing.firstName || '-'}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.LAST_NAME}</Col>
<Col md="4" id="review-billing-last-name">{customerDetails.billing.lastName || '-'}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.INVOICE_MAIL}</Col>
<Col md="4" id="review-billing-mail">{customerDetails.billing.invoiceMail || '-'}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.ADDRESS}</Col>
<Col md="4" id="review-billing-address">{customerDetails.billing.detailedAddress}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.CITY}</Col>
<Col md="4" id="review-billing-city">{customerDetails.billing.city}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.ZIP}</Col>
<Col md="4" id="review-billing-zip">{customerDetails.billing.zipCode}</Col>
</Row>
<Row className="cart-customer-main-info-row">
<Col md="4">{cartTexts.labels.COUNTRY}</Col>
<Col md="4" id="review-billing-country">{customerDetails.billing.countryName}</Col>
</Row>
</div>
</span>
}
<Row>
<Col xl={{size:3, offset:9}} sm="12" xs="12" className="button-cart">
{
(customerDetails && !isLoading) &&
<Button className="place-order-btn" onClick={()=>{this.setDialogParams(dialogContent)}}>Confirm order</Button>
}
</Col>
</Row>
{orderPlaced && orderPlacedRedirect && <Redirect push to="/" />}
</WiaasTableBody>
</WiaasTable>
}
</div>
);
}
}
const mapStateToProps = (state) => ({
orderInfo: state.cartReducer.orderInfo,
isLoading: state.cartReducer.isLoading,
orderPlaced: state.cartReducer.orderPlaced,
orderPlacedRedirect: state.cartReducer.orderPlacedRedirect,
orderProjects: state.orderProjectsReducer.orderProjects
});
export default connect(mapStateToProps)(CartReviewOrderContainer);

View File

@@ -0,0 +1,75 @@
import React, {Component} from 'react';
import {Col} from 'reactstrap';
import {connect} from 'react-redux';
import './style/CartStepsContainer.css';
import {fetchCartItems} from '../../actions/cart/cartActions';
import {cartTexts} from '../../constants/cartConstants';
class CartStepsContainer extends Component {
constructor(props) {
super(props);
this.handleStepChange = this.handleStepChange.bind(this);
}
componentDidMount() {
const isForSteps = true;
this.props.dispatch(fetchCartItems(isForSteps));
}
handleStepChange(stepDirection) {
if(stepDirection === 'next') {
if(this.props.nextStepAction) {
this.props.nextStepAction();
}
} else {
if(this.props.prevStepAction) {
this.props.prevStepAction();
}
}
}
render() {
const {cartSteps, currentStep, isLoading} = this.props;
const stepsLength = cartSteps && Object.keys(cartSteps).length - 1;
return (
<Col id="cart-steps-container" xl="12" lg="12" md="12" xs="12" sm="12">
{(cartSteps && cartSteps[currentStep] && cartSteps[currentStep].previous && !isLoading) &&
<span onClick={()=>{this.handleStepChange('previous')}} className="prev-btn">
<Col sm="12" xs="12">
{cartTexts.buttons.PREVIOUS}
</Col>
</span>
}
{cartSteps && Object.keys(cartSteps).map((stepKey, index) =>
<span key={'cart-step-' + stepKey}>
<Col xl="3" sm="12" xs="12" className={'step-name ' + cartSteps[stepKey].status}>
{(index + 1) + ' . ' + cartSteps[stepKey].name}
</Col>
{stepsLength !== index && <div className="steps-link"><div className="step-link"></div></div>}
</span>
)}
{(cartSteps && cartSteps[currentStep] && cartSteps[currentStep].next && !isLoading) &&
<span onClick={()=>{this.handleStepChange('next')}} className="next-btn">
<Col sm="12" xs="12" >
{cartTexts.buttons.NEXT}
</Col>
</span>
}
</Col>
);
}
}
const mapStateToProps = (state) => ({
cartItems: state.cartReducer.cartItems,
cartSteps: state.cartReducer.cartSteps,
currentStep: state.cartReducer.currentStep,
cartDocuments: state.cartReducer.cartDocuments,
nextStepAction: state.cartReducer.nextStepAction,
prevStepAction: state.cartReducer.prevStepAction,
isLoading: state.cartReducer.isLoading
});
export default connect(mapStateToProps)(CartStepsContainer);

View File

@@ -0,0 +1,99 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Container} from 'reactstrap';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import CartUploadDocument from './components/CartUploadDocument.jsx';
import './style/CartUploadDocumentsContainer.css';
import {cartMessages} from '../../constants/cartConstants';
import {updateMessages} from '../../actions/notification/notificationActions';
import {nextStep, setNextActionFct} from '../../actions/cart/cartActions';
const DOCUMENT_TYPES = {
TEMPLATE_QUESTINNAIRE: 1,
QUESTIONNAIRE: 2,
TEMPLATE_AGREEMENT: 6,
AGREEMENT: 7
};
class CartCustomerQuestionnaireContainer extends Component {
constructor(props) {
super(props);
this.getDocument = this.getDocument.bind(this);
this.handleStepChange = this.handleStepChange.bind(this);
}
componentDidMount() {
this.props.dispatch(setNextActionFct(this.handleStepChange));
}
getDocument(idDocumentType, idPackage, documents) {
return documents[idDocumentType] && documents[idDocumentType][idPackage]
? documents[idDocumentType][idPackage]
: null;
}
handleStepChange() {
if(this.props.cartDocuments && this.props.cartDocuments.areFilesUploaded) {
this.props.dispatch(nextStep());
} else {
this.props.dispatch(updateMessages([{code:'warning', message: 'DOCS_MISSING'}], cartMessages));
}
}
render() {
const {cartItems, cartDocuments, isLoading} = this.props;
const packages = cartItems ? cartItems.map((cartItem) => cartItem.idPackage) : [];
return (
<div id="cart-upload-documents-container">
<Container fluid={true}>
<Row>
<Col lg="12" xs="12">
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(cartItems && cartDocuments && !isLoading) &&
cartItems.map((cartItem, mapKey) =>
<div key={'cart-upload-' + mapKey}>
<WiaasBox mainTitle={cartItem.packageName} className="no-margin-box">
<div className="upload-layer">
<Row>
<CartUploadDocument
idDocumentType={DOCUMENT_TYPES.QUESTIONNAIRE}
packages={packages}
cartItem={cartItem}
templateDocument={this.getDocument(DOCUMENT_TYPES.TEMPLATE_QUESTINNAIRE, cartItem.idPackage, cartDocuments['templates'])}
uploadedDocument={this.getDocument(DOCUMENT_TYPES.QUESTIONNAIRE, cartItem.idPackage, cartDocuments['uploaded'])}
/>
<CartUploadDocument
idDocumentType={DOCUMENT_TYPES.AGREEMENT}
packages={packages}
cartItem={cartItem}
templateDocument={this.getDocument(DOCUMENT_TYPES.TEMPLATE_AGREEMENT, cartItem.idPackage, cartDocuments['templates'])}
uploadedDocument={this.getDocument(DOCUMENT_TYPES.AGREEMENT, cartItem.idPackage, cartDocuments['uploaded'])}
/>
</Row>
</div>
</WiaasBox>
</div>)
}
</Col>
</Row>
</Container>
</div>
);
}
}
const mapStateToProps = (state) => ({
cartItems: state.cartReducer.cartItems,
cartDocuments: state.cartReducer.cartDocuments,
isLoading: state.cartReducer.isLoading
});
export default connect(mapStateToProps)(CartCustomerQuestionnaireContainer);

View File

@@ -0,0 +1,49 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Form, FormGroup, Label, Input, Button} from 'reactstrap';
import {addProject} from '../../../actions/orderProjects/orderProjectsActions';
class AddOrderProject extends Component {
constructor(props) {
super(props);
this.state = {
projectName: ''
}
this.onProjectAdd = this.onProjectAdd.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const field = event.target.name;
const newProjectData = Object.assign({}, this.state);
newProjectData[field] = event.target.value;
this.setState(newProjectData);
}
onProjectAdd(){
this.props.dispatch(addProject(this.state));
}
render() {
return (<Form>
<FormGroup>
<Label for="projectName">Project Name <span className="required-icon">*</span></Label>
<Input
type="text"
onChange={this.handleChange}
name="projectName"
id="projectName"
value={this.state.projectName}
placeholder="Project Name"/>
</FormGroup>
<FormGroup>
<Button onClick={this.onProjectAdd}>Add Project</Button>
</FormGroup>
</Form>);
}
}
export default connect()(AddOrderProject);

View File

@@ -0,0 +1,40 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Button} from 'reactstrap';
import { WiaasTableRow, WiaasTableCol} from '../../../mainComponents/table/WiaasTable.jsx';
import {setBid} from '../../../actions/bids/bidsActions';
import {cartTexts} from '../../../constants/cartConstants';
class BidItem extends Component {
constructor(props) {
super(props);
this.onBidSelect = this.onBidSelect.bind(this);
}
onBidSelect(idBid){
this.props.dispatch(setBid(idBid, this.props.idCart));
}
render() {
const {bid, idSelectedBid} = this.props;
return (
<WiaasTableRow className="bid-item">
<WiaasTableCol header={cartTexts.labels.BID_NUMBER}>{bid.bidNumber}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.START_DATE}>{bid.startDate}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.END_DATE}>{bid.endDate}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.FIXED}>{bid.fixedPrice}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.RECURRENT}>{bid.recurrentPrice}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.SERVICES}>{bid.servicesPrice}</WiaasTableCol>
<WiaasTableCol header={cartTexts.labels.ACTIONS}>
{
idSelectedBid === bid.idBid
? <Button onClick={()=>{this.onBidSelect(null)}}><span className="fa fa-times remove-item"></span> {cartTexts.buttons.REMOVE}</Button>
: <Button onClick={()=>{this.onBidSelect(bid.idBid)}}>{cartTexts.buttons.USE}</Button>
}
</WiaasTableCol>
</WiaasTableRow>
);
}
}
export default connect()(BidItem);

View File

@@ -0,0 +1,34 @@
import React, {Component} from 'react';
import BidItem from './BidItem.jsx';
import {cartTexts} from '../../../constants/cartConstants';
import {WiaasTable, WiaasTableHeader, WiaasTableBody} from '../../../mainComponents/table/WiaasTable.jsx';
class BidsList extends Component {
getHeaders(){
return [
cartTexts.labels.BID_NUMBER,
cartTexts.labels.START_DATE,
cartTexts.labels.END_DATE,
cartTexts.labels.FIXED,
cartTexts.labels.RECURRENT,
cartTexts.labels.SERVICES,
cartTexts.labels.ACTIONS
];
}
render() {
const {bids, idCart, idSelectedBid} = this.props.params;
return (
<WiaasTable id="bids-list">
<WiaasTableHeader headers={this.getHeaders()}/>
<WiaasTableBody>
{
bids.map(bid => <BidItem key={'bid-' + bid.idBid} idSelectedBid={idSelectedBid} idCart={idCart} bid={bid}/>)
}
</WiaasTableBody>
</WiaasTable>
);
}
}
export default BidsList;

View File

@@ -0,0 +1,23 @@
import React, {Component} from 'react';
import {cartTexts} from '../../../constants/cartConstants';
class CartIcon extends Component {
render() {
let {cartCount} = this.props;
cartCount = parseInt(cartCount, 10);
return (
<span id="cart-count" className="items-cart-count">
{ cartCount === 0
? <span>{cartTexts.labels.EMPTY_CART_ICON}</span>
: cartCount === 1
? <span>1 item in cart</span>
: <span>{cartCount} {cartTexts.labels.ITEMS_IN_CART_ICON}</span>
}
</span>
);
}
}
export default CartIcon;

View File

@@ -0,0 +1,272 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Input, Tooltip, Row, Col} from 'reactstrap';
import '../style/Cart.css';
import {updateMessages} from '../../../actions/notification/notificationActions';
import {updateQuantity, removeCartItem} from '../../../actions/cart/cartActions';
import {setDialogContent, setDialogOpenFlag} from '../../../actions/dialog/dialogActions';
import {cartMessages, cartTexts} from '../../../constants/cartConstants';
import PackageBids from './PackageBids.jsx';
class CartItem extends Component {
constructor(props) {
super(props);
this.state = {
tooltipOpen: false,
itemQuantity: 1,
idCart: 0
};
this.toggleTooltip = this.toggleTooltip.bind(this);
this.removeItemFromCart = this.removeItemFromCart.bind(this);
this.resetQuantity = this.resetQuantity.bind(this);
this.sumPrices = this.sumPrices.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({itemQuantity: nextProps.cartItem.quantity});
this.sumPrices(nextProps.cartItem, nextProps.cartItem.quantity);
}
componentDidMount() {
this.setState({itemQuantity: this.props.cartItem.quantity});
this.sumPrices(this.props.cartItem, this.props.cartItem.quantity);
}
isQuantityValid(quantity) {
return quantity > 0 && quantity <= 100;
}
updateQuantity(cartItem, quantity) {
if(quantity) {
this.setState({itemQuantity: quantity});
if(this.isQuantityValid(quantity)) {
this.sumPrices(cartItem, quantity);
this.props.dispatch(updateQuantity(cartItem, quantity));
} else {
this.props.dispatch(updateMessages([{code: 'error', message: 'INVALID_QUANTITY'}], cartMessages));
}
} else {
this.setState({itemQuantity: ''});
}
}
resetQuantity(cartItem) {
if(!this.state.itemQuantity || !this.isQuantityValid(this.state.itemQuantity)) {
this.updateQuantity(cartItem, 1);
}
}
removeItemFromCart() {
this.props.dispatch(removeCartItem(this.state.idCart));
}
setDialogParams(cartItem, dialogContent) {
this.setState({idCart: cartItem.idCart});
this.props.dispatch(setDialogOpenFlag(true));
dialogContent.bodyVariable = cartItem.packageName;
this.props.dispatch(setDialogContent(dialogContent));
}
toggleTooltip() {
this.setState({
tooltipOpen: !this.state.tooltipOpen
});
}
sumPrices(cartItem, quantity) {
let total = 0;
let oldTotal = null;
let totalRecurrent = 0;
let oldTotalRecurrent = null;
const selectedBid = cartItem.bids.find((bid) => {
return cartItem.idSelectedBid === bid.idBid;
});
if(selectedBid){
total += selectedBid.fixedPrice;
totalRecurrent += selectedBid.servicesPrice + selectedBid.recurrentPrice;
oldTotal = cartItem.fixedPrice;
oldTotalRecurrent = cartItem.servicesPrice + cartItem.recurentPrice;
}else{
total += cartItem.fixedPrice;
totalRecurrent += cartItem.servicesPrice + cartItem.recurentPrice;
}
if(cartItem.options.length) {
cartItem.options.forEach((packageOption) => {
if(Object.keys(packageOption.prices).length) {
total += parseFloat(packageOption.prices.fixedExtra);
totalRecurrent += parseFloat(packageOption.prices.recurentExtra) + parseFloat(packageOption.prices.servicesExtra);
}
});
}
if(cartItem.additionalPackages.length) {
cartItem.additionalPackages.forEach((additionalPackage) => {
if(Object.keys(additionalPackage).length) {
total += parseFloat(additionalPackage.prices.fixedExtra);
totalRecurrent += parseFloat(additionalPackage.prices.recurentExtra) + parseFloat(additionalPackage.prices.servicesExtra);
}
});
}
this.setState({
fixedPrice: total * quantity,
recurrentPrice: totalRecurrent * quantity,
fixedOldPrice: oldTotal !== null ? oldTotal * quantity : null,
recurrentOldPrice: oldTotalRecurrent !== null ? oldTotalRecurrent * quantity: null
});
}
getPriceClass(price){
return price !== null ? 'new-price' : 'price';
}
render() {
const dialogContent = {
buttons: [
{
color: 'success',
action: this.removeItemFromCart,
name: cartTexts.buttons.YES,
id: 'remove-item-confirmation'
}, {
color: 'secondary',
name: cartTexts.buttons.CANCEL,
id: 'cancel-remove-item'
}
],
header: cartTexts.labels.REMOVE_ITEM_HEADER,
body: cartTexts.labels.REMOVE_ITEM_TEXT
};
const {cartItem, isFormDisabled} = this.props;
const cartItemNo = this.props.cartItemNo ? this.props.cartItemNo + 1 : 1;
return (
<div id={"item-row-in-cart-" + cartItem.idCart} className="cart-show-items">
<Row className="cart-item-details">
<Col id={"item-package-name-" + cartItem.idCart}
xl="8" lg="8" md="7" sm="12" xs="12"
className="item-name">
{cartItemNo}. {cartItem.packageName}
<PackageBids idSelectedBid={cartItem.idSelectedBid} idCart={cartItem.idCart} bids={cartItem.bids}/>
</Col>
<Col xl="3" lg="3" md="3" sm="9" xs="9" id={"item-quantity-" + cartItem.idCart}>
{ isFormDisabled
? cartItem.quantity === 1
? cartItem.quantity + ' pc'
: cartItem.quantity + ' pcs'
: <Input className="quantity-field"
value={this.state.itemQuantity}
type="number"
step="1"
onChange={e => this.updateQuantity(cartItem, e.target.value)}
onBlur={e => this.resetQuantity(cartItem, e.target.value)}
/>
}
</Col>
<Col id={"item-remove-btn-" + cartItem.idCart} xl="1" lg="1" md="1" sm="1" xs="1">
<i id={'remove-item-' + cartItem.idCart + '-' + cartItem.idPackage}
className="fa fa-times remove-item"
aria-hidden="true"
onClick={() => this.setDialogParams(cartItem, dialogContent) }></i>
<Tooltip target={'remove-item-' + cartItem.idCart + '-' + cartItem.idPackage}
placement="bottom"
isOpen={this.state.tooltipOpen}
toggle={this.toggleTooltip}>
{cartTexts.labels.REMOVE_FROM_CART}
</Tooltip>
</Col>
</Row>
{ (!cartItem.areAdditionalAvailable || !cartItem.areOptionsAvailable || cartItem.status === 'not-available') &&
<Row className="cart-item-small-details">
<Col id={"item-warning-sign-remove-package-name-" + cartItem.idPackage}
xl="12" lg="12" md="12" sm="12" xs="12"
className="not-available">
{cartTexts.labels.PACKAGE_UNAVAILABLE}
</Col>
</Row>
}
{cartItem.options.length > 0 &&
cartItem.options.map((packageOption) =>
<Row className="cart-item-small-details" key={"package-option-" + cartItem.idCart + "-" + packageOption.idOptionPackage}>
<Col lg={{offset: 1, size: 4}} xs={{offset: 1, size: 4}}>
{packageOption.groupName}:
</Col>
<Col lg="7" xs="7" id={"item-option-" + cartItem.idCart + "-" + packageOption.idOptionPackage}>
{packageOption.packageName}
</Col>
</Row>
)
}
{cartItem.additionalPackages.length > 0 &&
cartItem.additionalPackages.map((additionalPackage) =>
<Row className="cart-item-small-details" key={"additional-package-" + cartItem.idCart + "-" + additionalPackage.idAdditionalPackage}>
<Col lg={{offset: 1, size: 4}} xs={{offset:1, size: 4}}>
{cartTexts.labels.ADDITIOONAL_PACKAGE}:
</Col>
<Col lg="7" xs="7" id={"item-additional-" + cartItem.idCart + "-" + additionalPackage.idAdditionalPackage}>
{additionalPackage.packageName}
</Col>
</Row>
)
}
<Row className="cart-item-small-details">
<Col lg={{offset: 1, size: 4}} xs={{offset:1, size: 4}}>Payment type:</Col>
<Col lg="7" xs="7" id={"item-payment-type-" + cartItem.idCart}>
{cartItem.payType}
</Col>
</Row>
<Row className="cart-item-small-details">
<Col lg={{offset: 1, size: 4}} xs={{offset:1, size: 4}}>Price:</Col>
<Col lg="7" xs="7" id={"item-price-" + cartItem.idCart}>
<table className="price-table">
<thead>
<tr>
<th>{cartTexts.labels.ON_DELIVERY}</th>
<th>{cartTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{
this.state.fixedPrice &&
<div>
{
this.state.fixedOldPrice !== null &&
<div className="old-price">{this.state.fixedOldPrice}</div>
}
<div className={this.getPriceClass(this.state.fixedOldPrice)}>
{this.state.fixedPrice.toLocaleString() + ' ' + cartItem.country.currency}
</div>
</div>
}
</td>
<td>
{
this.state.recurrentPrice &&
<div>
{
this.state.recurrentOldPrice !== null &&
<div className="old-price">{this.state.recurrentOldPrice}</div>
}
<div className={this.getPriceClass(this.state.recurrentOldPrice)}>
{this.state.recurrentPrice.toLocaleString() + ' ' + cartItem.country.currency}
</div>
</div>
}
</td>
</tr>
</tbody>
</table>
</Col>
</Row>
</div>
);
}
}
export default connect()(CartItem);

View File

@@ -0,0 +1,79 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Col} from 'reactstrap';
import {uploadOrderDocument, badFile} from '../../../actions/cart/cartActions';
import Dropzone from 'react-dropzone';
import {API_SERVER} from '../../../config';
import FileDownloader from '../../../helpers/FileDownloader';
import {cartTexts} from '../../../constants/cartConstants';
const fileHandler = new FileDownloader();
const documentTypes = {
2 : 'Customer Questionnaire',
7 : 'Customer Agreement'
};
class CartUploadDocument extends Component {
constructor(props) {
super(props);
this.uploadFile = this.uploadFile.bind(this);
}
uploadFile(idPackage, idDocumentType,acceptedFiles, rejectedFiles, packages){
const self = this;
acceptedFiles.forEach(file => {
self.props.dispatch(uploadOrderDocument(idPackage, idDocumentType, file, packages));
});
if(rejectedFiles && rejectedFiles.length) {
this.props.dispatch(badFile());
}
}
downloadDocument(document){
const fileUrl = `${API_SERVER}/utils/api/downloadFile?idDocument=${document.idDocument}&fileName=${document.documentName}.${document.extension}`
const fileName = document.documentName + '.' + document.extension;
fileHandler.download(fileUrl, fileName);
}
render() {
const {idDocumentType, cartItem, uploadedDocument, templateDocument, packages} = this.props;
return (<Col lg="6">
{
templateDocument &&
<div>
<Dropzone className="upload-file-drop-zone"
accept=".pdf,.docx,.doc,.xlsx,.xls,.odt,.ods"
onDrop={(acceptedFiles, rejectedFiles)=>{this.uploadFile(cartItem.idPackage, idDocumentType, acceptedFiles, rejectedFiles, packages)}}>
{
uploadedDocument
? <h6 className="drop-zone-text uploaded">{cartTexts.labels.FILE_UPLOADED_TEXT} {documentTypes[idDocumentType]} {cartTexts.labels.FILE}</h6>
: <h6 className="drop-zone-text">{cartTexts.labels.NO_FILE_UPLOAD_TEXT_1} {documentTypes[idDocumentType]} {cartTexts.labels.NO_FILE_UPLOAD_TEXT_2}</h6>
}
</Dropzone>
{
uploadedDocument &&
<div>
<div className="cart-subtitle">{cartTexts.labels.UPLOADED}:</div>
<div className="document-link" onClick={() => {this.downloadDocument(uploadedDocument)}}>
<i className="fa fa-file" aria-hidden="true"></i>
{' ' + uploadedDocument.documentName + ' (' + uploadedDocument.extension + ')'}
</div>
</div>
}
<div className="cart-subtitle">{cartTexts.labels.TEMPLATES}:</div>
<div className="document-link" onClick={() => {this.downloadDocument(templateDocument)}}>
<i className="fa fa-file" aria-hidden="true"></i>
{' ' + templateDocument.documentName + ' (' + templateDocument.extension + ')'}
</div>
</div>
}
</Col>);
}
}
export default connect()(CartUploadDocument);

View File

@@ -0,0 +1,71 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Button, Row, Col} from 'reactstrap';
import Select from 'react-select';
import AddOrderProject from './AddOrderProject.jsx';
import {getOrderProjects} from '../../../actions/orderProjects/orderProjectsActions';
import {setDialogOpenFlag, setDialogContent} from '../../../actions/dialog/dialogActions';
import '../style/OrderProjects.css';
class OrderProjectsSelect extends Component {
constructor(props) {
super(props);
this.openDialog = this.openDialog.bind(this);
}
componentDidMount(){
this.props.dispatch(getOrderProjects());
}
openDialog(){
const dialogContent = {
header: 'Add Project',
TagName: AddOrderProject,
class: 'user-dialog',
hasCloseIcon: true
};
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
render() {
const {orderProjects, isLoading, idProject, handleChange} = this.props;
return (<Col md={4} id="order-projects">
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{orderProjects && !isLoading &&
<Row>
<Col md={10}>
<Select value={idProject}
name="idProject"
className="order-project-select"
placeholder="Project..."
options={orderProjects}
clearable={true}
onChange={(project) => {handleChange(project)}}/>
</Col>
<Col md={2}>
<Button onClick={this.openDialog} className="actions-button">
<i className="fa fa-plus" aria-hidden="true"></i>
</Button>
</Col>
</Row>
}
</Col>);
}
}
const mapStateToProps = (state) => ({
orderProjects: state.orderProjectsReducer.orderProjects,
isLoading: state.orderProjectsReducer.isLoading
});
export default connect(mapStateToProps)(OrderProjectsSelect);

View File

@@ -0,0 +1,47 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {cartTexts} from '../../../constants/cartConstants';
import {setDialogOpenFlag, setDialogContent} from '../../../actions/dialog/dialogActions';
import BidsList from './BidsList.jsx';
import '../style/PacakgeBids.css';
class PackageBids extends Component {
constructor(props){
super(props);
this.openDialog = this.openDialog.bind(this);
}
openDialog(){
const dialogContent = {
header: cartTexts.labels.AVAILABLE_BIDS,
TagName: BidsList,
class: 'bid-dialog',
hasCloseIcon: true,
params: {
bids: this.props.bids,
idCart: this.props.idCart,
idSelectedBid: this.props.idSelectedBid
}
};
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
render() {
const {bids} = this.props;
return (
<div id="cart-bids" className="cart-bids ">
{
bids && bids.length > 0 &&
<span onClick={this.openDialog} className="cart-bids-text">
{cartTexts.labels.BID_AVAILABLE}
{' '}<span className="fa fa-arrow-circle-left"></span>
</span>
}
</div>);
}
}
export default connect()(PackageBids);

View File

@@ -0,0 +1,50 @@
import React, {Component} from 'react';
import {FormGroup, Input} from 'reactstrap';
class SelectBillingAddress extends Component {
constructor(props) {
super(props);
this.handleOptionChange = this.handleOptionChange.bind(this);
}
handleOptionChange(){
this.props.handleBillingChange(this.props.billingAddress);
}
render() {
const {billingAddress, openAddressDialog, idSelectedBillingAddress} = this.props;
return (
<div className="address-row">
<FormGroup check>
<div>
<div className="address-text">
<Input id={'billing-addres-check-' + billingAddress.id} checked={idSelectedBillingAddress === billingAddress.id} type="radio" onChange={this.handleOptionChange} name="billingAddress" />
<div className="small-address-title">
{billingAddress.firstName} {billingAddress.lastName}
{
billingAddress.invoiceMail &&
<span>( {billingAddress.invoiceMail} )</span>
}
</div>
<div>
{billingAddress.countryName}, {billingAddress.city}, {billingAddress.detailedAddress}, {billingAddress.zipCode}
</div>
</div>
<div className="address-icons">
<i className="fa fa-pencil control-icon edit-address-btn"
onClick={()=> {openAddressDialog('edit', billingAddress)}}
id="edit-address-btn"
aria-hidden="true"></i>
</div>
</div>
</FormGroup>
</div>
);
}
}
export default SelectBillingAddress;

View File

@@ -0,0 +1,41 @@
import React, {Component} from 'react';
import {FormGroup, Input} from 'reactstrap';
class SelectDeliveryAddress extends Component {
constructor(props) {
super(props);
this.handleOptionChange = this.handleOptionChange.bind(this);
}
handleOptionChange(){
this.props.handleDeliveryChange(this.props.profileAddress);
}
render() {
const {profileAddress, openAddressDialog, idSelectedDeliveryAddress} = this.props;
return (
<div className="address-row">
<FormGroup check>
<div>
<div className="address-text">
<Input id={'delivery-addres-check-' + profileAddress.id} checked={idSelectedDeliveryAddress === profileAddress.id} type="radio" onChange={this.handleOptionChange} name="deliveryAddress" />
{profileAddress.countryName}, {profileAddress.city}, {profileAddress.detailedAddress}, {profileAddress.zipCode}
</div>
<div className="address-icons">
<i className="fa fa-pencil control-icon edit-address-btn"
onClick={()=> {openAddressDialog('edit', profileAddress)}}
id="edit-address-btn"
aria-hidden="true"></i>
</div>
</div>
</FormGroup>
</div>
);
}
}
export default SelectDeliveryAddress;

View File

@@ -0,0 +1,170 @@
@import '../../../styleConstants.scss';
#cart-count {
vertical-align: middle;
}
#cart-container {
padding: 2rem;
}
#cart-items-container {
border-radius: $box-radius;
.cart-show-items {
padding-top: 1rem;
}
.quantity-field {
margin-left: 2%;
display: inline-block;
max-width: 5rem;
}
.remove-item {
color: $redColor;
cursor: pointer;
font-size: 1.3rem;
padding-top: 0.375rem;
line-height: 1.5rem;
}
.cart-item-details {
padding-top: 0.5rem;
}
.cart-item-small-details {
padding-top: 0.5rem;
font-size: $font-size-msmall;
}
.item-name {
font-weight: $font-weight;
}
.cart-total-price {
padding: 1rem 0;
font-size: $font-size-big;
}
.price-table th{
padding: 0.5rem;
}
.price-table td{
border-top: 1px solid $darkGreyColor;
padding: 0.5rem;
}
.not-available {
margin-left: 0.2rem;
color: $not-available-status-color;
font-weight: $font-weight;
font-size: $font-size-msmall;
}
.old-price{
text-decoration: line-through;
font-size: $font-size-msmall;
}
.new-price{
color: $redColor;
font-weight: $font-weight;
}
}
#cart-review-order-container {
border-radius: $box-radius;
.cart-customer-main-info-row {
padding: 0.5rem;
text-align: left;
}
.place-order-btn {
cursor: pointer;
background-color: $done-status-color;
width: 100%;
float: right;
font-weight: $font-weight;
}
.button-cart {
text-align: right;
padding: 1rem;
}
}
#go-to-co-market-text {
color: $warmGreyColor;
}
#cart-add-new-delivery-address {
margin: 1rem;
}
#cart-customer-details-container {
.cart-customer-main-info-row {
padding: 0.5rem;
text-align: left;
}
.country-address {
padding: 0;
}
.next-btn, .prev-btn {
cursor: pointer;
background-color: $whiteColor;
color: $darkGreyColor;
}
.next-btn {
float: right;
}
.prev-btn {
float: left;
}
.address-row {
position: relative;
border-bottom: 1px solid $divider-color;
padding: 0.5rem 0;
}
.address-text {
position: relative;
display: inline-block;
margin-left: 2rem;
width: 80%;
}
.address-icons{
position: absolute;
min-width: 3rem;
display: inline-block;
text-align: right;
width: 15%;
}
.control-icon {
margin-right: 2rem;
font-size: $font-size-big;
cursor: pointer;
}
.add-address-btn{
cursor: pointer;
margin: 1rem 0;
}
.cart-address-box {
margin-top: 2rem;
}
}
#cart-customer-main-information-container {
border-radius: $box-radius;
}

View File

@@ -0,0 +1,165 @@
@import '../../../styleConstants.scss';
#cart-steps-container {
padding: 1rem 1rem 2rem;
text-align: center;
.step-name {
cursor: default;
padding: 1rem;
display: inline-block;
border-radius: 4px;
color: $whiteColor;
}
.steps-link {
display: inline;
width: 2rem;
height: 0.15rem;
position: unset;
}
.step-link {
display: inline-block;
background: $borderColor;
height: 0.15rem;
width: 2rem;
}
.active {
background: $lightGreenColor;
}
.inactive {
background: $lightGreyColor;
}
.completed {
background: $blueColor;
}
.progress-main-bar {
background: none;
height: 1.5rem;
font-size: $font-size-small;
}
.progress-bar-item {
border-radius: 0 15px 15px 0;
overflow: hidden;
margin-right: 0.5rem;
height: 100%;
}
.next-btn,
.prev-btn {
cursor: pointer;
background-color: $whiteColor;
color: $darkGreyColor;
border: 0.1rem $border-grey solid;
padding: 0.95rem;
display: inline-block;
border-radius: $box-radius;
font-weight: $font-weight;
}
.next-btn {
margin-left: 2rem;
}
.prev-btn {
margin-right: 2rem;
}
}
#cart-items-container {
padding-top: 1rem;
}
@media all and (max-width: 1494px) {
#cart-steps-container {
.steps-link {
position: relative;
height: 1.5rem;
display: block;
width: 2rem;
height: 1.5rem;
top: 0;
left: 49%;
}
.step-link {
background: $borderColor;
width: 3px;
height: 1.5rem;
z-index: 0;
}
.next-btn {
display: block;
width: fit-content;
margin-top: 1rem;
margin-left: 45%;
}
.prev-btn {
display: block;
width: fit-content;
margin-bottom: 1rem;
margin-left: 44%;
}
}
}
@media all and (min-width: 992px) and (max-width: 1200px) {
#cart-steps-container {
.step-name {
width: 30%;
}
.steps-link {
position: relative;
height: 1.5rem;
display: block;
width: 2rem;
height: 1.5rem;
top: 0;
left: 49%;
}
.step-link {
background: $borderColor;
width: 3px;
height: 1.5rem;
z-index: 0;
}
}
}
@media all and (max-width: 992px) {
#cart-steps-container {
.next-btn {
display: block;
margin-top: 1rem;
margin-left: 0;
width: 100%;
}
.prev-btn {
display: block;
margin-bottom: 1rem;
margin-left: 0;
width: 100%;
}
}
#cart-customer-details-container,
#cart-review-all-order-container,
#cart-upload-documents-container {
border-top: 0.2rem solid $borderColor;
}
}
@media all and (max-width: 768px) {
#cart-steps-container {
.step-name {
display: block;
}
}
}

View File

@@ -0,0 +1,54 @@
@import '../../../styleConstants.scss';
#cart-upload-documents-container{
border-radius: $box-radius;
background: $whiteColor;
.upload-file-drop-zone {
width: 100%;
border: 3px dashed $borderColor;
margin-top: 1rem;
height: 5rem;
cursor: pointer;
}
.drop-zone-text {
text-align: center;
padding: 1.3rem 0.5rem;
}
.uploaded {
color: #155724;
}
.next-btn {
float: right;
cursor: pointer;
background-color: $whiteColor;
color: $darkGreyColor;
}
.cart-subtitle {
font-weight: $font-weight;
padding-top: 0.3rem;
}
}
.upload-layer{
background: $whiteColor;
padding-bottom: 1rem;
.document-link{
cursor: pointer;
}
}
@media all and (max-width: 480px) {
#cart-upload-documents-container {
margin-top: 1rem;
.upload-file-drop-zone {
height: 8rem;
}
}
}

View File

@@ -0,0 +1,20 @@
@import '../../../styleConstants.scss';
#order-projects{
padding: 0;
margin: 0;
.order-project-select{
width: 90%;
}
.actions-button {
background-color: $whiteColor;
color: $darkGreyColor;
cursor: pointer;
}
.actions-button:hover{
background: rgba(113, 194, 191, 0.5);
}
}

View File

@@ -0,0 +1,13 @@
@import '../../../styleConstants.scss';
#cart-bids{
.cart-bids-text {
color: $redColor;
font-size: $font-size-msmall;
cursor: pointer;
}
}
.bid-dialog{
width: 70%;
}

View File

@@ -0,0 +1,27 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Container} from 'reactstrap';
import CoMarketPackagesContainer from './CoMarketPackagesContainer.jsx';
import CoMarketPackageDetailsContainer from './CoMarketPackageDetailsContainer.jsx';
import {setPackageFromUrl} from '../../actions/coMarket/coMarketActions';
import './style/CoMarket.css'
class CoMarketContainer extends Component {
componentDidMount(){
this.props.dispatch(setPackageFromUrl(this.props.match.params.idPackage));
}
render() {
const urlParams = this.props.match.params;
return (<Container fluid={true} id="co-market-container">
{
urlParams.idPackage ?
<CoMarketPackageDetailsContainer idPackage={urlParams.idPackage} idCommercialLead={urlParams.idCommercialLead}/> :
<CoMarketPackagesContainer/>
}
</Container>);
}
}
export default connect()(CoMarketContainer);

View File

@@ -0,0 +1,45 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Input} from 'reactstrap';
import {fetchShopPackages} from '../../actions/coMarket/coMarketPackagesActions';
import {coMarketTexts} from '../../constants/coMarketConstants';
class CoMarketNavContainer extends Component {
constructor(props) {
super(props);
this.handleSearchChange = this.handleSearchChange.bind(this);
this.state = {
searchValue : ''
};
}
handleSearchChange(event) {
this.setState({searchValue: event.target.value});
this.props.dispatch(fetchShopPackages(this.props.selectedCommercialLead, event.target.value));
}
render() {
return (
<Row className="co-market-nav">
<Col xl="4" lg="3" md="3" sm="12" xs="12" className="co-market-nav-buttons">
<div className="co-market-nav-div">
{coMarketTexts.labels.NEW_PRODUCTS}
</div>
</Col>
<Col xl="4" lg="4" md="4" xs="12" className="search-layer">
<div className="co-market-nav-div">
<i className="search-package-icon fa fa-search" aria-hidden="true"></i>
<Input className="wiaas-input" onChange={this.handleSearchChange} value={this.state.searchValue} placeholder={coMarketTexts.labels.SEARCH_PACKAGE}/>
</div>
</Col>
</Row>
);
}
}
const mapStateToProps = (state) => ({
selectedCommercialLead: state.coMarketPackagesReducer.selectedCommercialLead
});
export default connect(mapStateToProps)(CoMarketNavContainer);

View File

@@ -0,0 +1,109 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Button} from 'reactstrap';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import {fetchPackageDetails, addToCart} from '../../actions/coMarket/coMarketPackageDetailsActions';
import PackageInfo from './components/PackageInfo.jsx';
import PackagePrice from './components/PackagePrice.jsx';
import PackageOptions from './components/PackageOptions.jsx';
import AdditionalPackages from './components/AdditionalPackages.jsx';
import AgreementOptions from './components/AgreementOptions.jsx';
import {coMarketTexts} from '../../constants/coMarketConstants';
import './style/CoMarketPackageDetailsContainer.css';
class CoMarketPackageDetailsContainer extends Component {
constructor(props) {
super(props);
this.handleAddToCart = this.handleAddToCart.bind(this);
this.state = {
selectedOptions: {}
};
}
handleAddToCart() {
const addParams = {
selectedPackage: this.props.selectedPackage,
selectedAgreement: this.props.selectedAgreement,
selectedOptions: this.props.selectedOptions,
selectedAdditionals: this.props.selectedAdditionals
};
this.props.dispatch(addToCart(addParams));
}
componentDidMount() {
const {idPackage, idCommercialLead} = this.props;
this.props.dispatch(fetchPackageDetails({idPackage, idCommercialLead}));
}
render() {
const {selectedPackage, selectedAgreement, messages, isLoading} = this.props;
return(
<div id="co-market-package-details">
<Row>
<Col xl="12" lg="12">
<WiaasBox>
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(selectedPackage && !isLoading) &&
<Row>
<Col xl="6" lg="5" md="12" xs="12">
<PackageInfo
packageInfo={selectedPackage.packageInfo}
documents={selectedPackage.documents} />
</Col>
<Col xl="6" lg="7" md="12" xs="12">
<div id="shop-package-buy-info">
<PackagePrice
country={selectedPackage.country} />
{
selectedPackage.groups && Object.keys(selectedPackage.groups).length > 0 && selectedAgreement &&
<PackageOptions
groups={selectedPackage.groups}
country={selectedPackage.country}/>
}
{
selectedPackage.additionalPackages && selectedPackage.additionalPackages.length > 0 && selectedAgreement &&
<AdditionalPackages
additionalPackages={selectedPackage.additionalPackages}
country={selectedPackage.country}/>
}
<AgreementOptions
prices={selectedPackage.prices}
country={selectedPackage.country}/>
<Button id="add-to-cart-btn"
className="add-to-cart-btn"
onClick={this.handleAddToCart}
color="secondary">
<i className="fa fa-shopping-cart" aria-hidden="true"></i>
{' '}{coMarketTexts.buttons.ADD_TO_CART}
</Button>
{messages && <span>{messages[0].message}</span>}
</div>
</Col>
</Row>
}
</WiaasBox>
</Col>
</Row>
</div>
);
}
}
const mapStateToProps = (state) => ({
selectedPackage: state.coMarketPackageDetailsReducer.selectedPackage,
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedOptions: state.coMarketPackageDetailsReducer.selectedOptions,
selectedAdditionals: state.coMarketPackageDetailsReducer.selectedAdditionals,
messages: state.coMarketPackageDetailsReducer.messages,
isLoading: state.coMarketPackageDetailsReducer.isLoading
});
export default connect(mapStateToProps)(CoMarketPackageDetailsContainer);

View File

@@ -0,0 +1,62 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col} from 'reactstrap';
import ShopItem from './components/ShopItem.jsx';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import CoMarketNavContainer from './CoMarketNavContainer.jsx';
import {fetchShopPackages} from '../../actions/coMarket/coMarketPackagesActions';
class CoMarketPackagesContainer extends Component {
componentDidMount() {
this.props.dispatch(fetchShopPackages(this.props.selectedCommercialLead));
}
render() {
const {shopPackages, selectedCommercialLead, isLoading} = this.props;
return (
<div id="co-market-shop">
<Row>
<Col xl="8" lg="8" md="8" sm="12" xs="12">
<WiaasBox id="co-market-big-commercial">
<img className="description-photo" src="https://res.cloudinary.com/co-market/image/upload/v1524472688/Co-Market/CoMarketStartsida_MAIN.jpg" alt="big-commercial"/>
</WiaasBox>
</Col>
<Col xl="4" lg="4" md="4" sm="12" xs="12">
<WiaasBox id="co-market-commercials">
<img alt="wiaas commercial" src="https://res.cloudinary.com/co-market/image/upload/v1524472688/Co-Market/CoMarketStartsida_OFFERCoor.jpg" className="commercial-photo"/>
</WiaasBox>
</Col>
</Row>
<Row>
<Col xl="8" lg="12" md="12">
<WiaasBox id="co-market-packages" customHeader={CoMarketNavContainer}>
<Row>
{
isLoading &&
<Col xl="12" className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</Col>
}
{
(shopPackages && !isLoading) &&
shopPackages.map((shopPackage, mapKey) => <ShopItem key={shopPackage.idPackage}
idCommercialLead={selectedCommercialLead.value}
shopPackage={shopPackage}/>)
}
</Row>
</WiaasBox>
</Col>
</Row>
</div>
);
}
}
const mapStateToProps = (state) => ({
shopPackages: state.coMarketPackagesReducer.shopPackages,
selectedCommercialLead: state.coMarketPackagesReducer.selectedCommercialLead,
isLoading: state.coMarketPackagesReducer.isLoading
});
export default connect(mapStateToProps)(CoMarketPackagesContainer);

View File

@@ -0,0 +1,131 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col} from 'reactstrap';
import {selectAdditional, removetAdditional} from '../../../actions/coMarket/coMarketPackageDetailsActions';
import PriceHelper from '../../../helpers/coMarket/PriceHelper';
import {coMarketTexts} from '../../../constants/coMarketConstants';
const priceHelper = new PriceHelper();
class AdditionalPackageItem extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.handleOptionChange = this.handleOptionChange.bind(this);
this.isChecked = this.isChecked.bind(this);
this.state = {
popoverOpen: false
};
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
handleOptionChange() {
if(!this.isChecked()){
this.props.dispatch(selectAdditional(this.props.additionalPackage))
}else{
this.props.dispatch(removetAdditional(this.props.additionalPackage));
}
}
isChecked() {
return this.props.selectedAdditionals
&& this.props.selectedAdditionals.includes(this.props.additionalPackage);
}
isAvailable() {
return this.props.additionalPackage.prices.find(price => {
return price.idPaymentType === this.props.selectedAgreement.idPaymentType;
});
}
getClass(isChecked) {
return isChecked ?
'selected-option' :
'';
}
formatName(name) {
return name.length > 39 ? name.substring(0, 40) + '...' : name;
}
render() {
const {additionalPackage, country} = this.props;
const isAvailable = this.isAvailable();
const selectedPrice = priceHelper.getSelectedPrice(additionalPackage, this.props.selectedAgreement);
const isChecked = this.isChecked();
return (
<Col xl="4" lg="4" md="6" xs="6" className="option-value">
<div className={'option-selection ' + this.getClass(isChecked)}>
<Label check>
<Input type="checkbox"
onChange={this.handleOptionChange}
name={'package-option-'+ additionalPackage.idAdditionalPackage}
className="package-option-input"/>
<div className="option-name">{this.formatName(additionalPackage.packageName)}</div>
<div className="option-description" dangerouslySetInnerHTML={{__html: additionalPackage.shortDescription}}></div>
{
priceHelper.hasFixedPrice(selectedPrice) &&
<div className="option-prices">
<table className="price-table option-price-table">
<thead>
<tr>
<th>{coMarketTexts.labels.ON_DELIVERY}</th>
<th>{coMarketTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{selectedPrice.fixedExtra > 0 ? selectedPrice.fixedExtra.toLocaleString() : 0} {country.currency}</td>
<td>
<div className="option-value-text monthly-price">
{
priceHelper.hasRecurrentPrice(selectedPrice)
? priceHelper.sumPrices([selectedPrice.recurentExtra, selectedPrice.servicesExtra]).toLocaleString() + ' '
: 0
} {country.currency}
</div>
</td>
</tr>
</tbody>
</table>
</div>
}
</Label>
{
!isAvailable &&
<span className="not-available">
({coMarketTexts.labels.NOT_AVAILABLE})
<i className="price-info-btn fa fa-info-circle"
aria-hidden="true"
id={'info-additonal-' + additionalPackage.idAdditionalPackage}
onClick={this.toggle}></i>
</span>
}
</div>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-additonal-' + additionalPackage.idAdditionalPackage}
toggle={this.toggle}>
<PopoverBody>
{coMarketTexts.labels.SELECTION_NOT_AVAILABLE}
</PopoverBody>
</Popover>
</Col>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedAdditionals: state.coMarketPackageDetailsReducer.selectedAdditionals
});
export default connect(mapStateToProps)(AdditionalPackageItem);

View File

@@ -0,0 +1,26 @@
import React, {Component} from 'react';
import {Row} from 'reactstrap';
import AdditionalPackageItemContainer from './AdditionalPackageItemContainer.jsx';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class AdditionalPackages extends Component {
render() {
const {additionalPackages, country} = this.props;
return (
<div className="shop-package-options">
<h6 className="shop-package-label options-header">{coMarketTexts.labels.ADDITIONAL_PACKAGES}:</h6>
<Row>
{
additionalPackages.map(additionalPackage => {
return <AdditionalPackageItemContainer key={'additonal-' + additionalPackage.idAdditionalPackage}
country={country}
additionalPackage={additionalPackage}/>
})
}
</Row>
</div>
);
}
}
export default AdditionalPackages;

View File

@@ -0,0 +1,127 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col} from 'reactstrap';
import {selectAgreement} from '../../../actions/coMarket/coMarketPackageDetailsActions';
import PriceHelper from '../../../helpers/coMarket/PriceHelper';
import {coMarketTexts} from '../../../constants/coMarketConstants';
const priceHelper = new PriceHelper();
class AgreementOptionItem extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.handleOptionChange = this.handleOptionChange.bind(this);
this.state = {
popoverOpen: false
};
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
handleOptionChange() {
this.props.dispatch(selectAgreement(this.props.price));
}
getClass(selectedAgreement, price) {
return selectedAgreement.idPaymentType === price.idPaymentType ?
'selected-option' :
'';
}
render() {
const {price, selectedAgreement, country} = this.props;
return (
<Col xl="4" lg="4" md="6" xs="6" className="option-value">
<div className={'option-selection ' + this.getClass(selectedAgreement, price)}>
<Label check>
<Input type="radio"
name="price-type"
onChange={this.handleOptionChange}
checked={selectedAgreement.idPaymentType === price.idPaymentType}
value={price.idPaymentType}
className="price-type-option"/>
<div className="option-name">{price.payType}</div>
<div className="option-prices">
<table className="price-table option-price-table">
<thead>
<tr>
<th>{coMarketTexts.labels.ON_DELIVERY}</th>
<th>{coMarketTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{price.fixedExtra.toLocaleString()} {country.currency}</td>
<td>
{
priceHelper.hasRecurrentPrice(price)
? <div className="option-value-text monthly-price">
{priceHelper.sumPrices([price.recurentExtra, price.servicesExtra]).toLocaleString()} {country.currency}
</div>
: <div className="option-value-text monthly-price"> 0 </div>
}
</td>
</tr>
</tbody>
</table>
</div>
</Label>
<i className="price-info-btn fa fa-info-circle"
aria-hidden="true"
id={'info-additonal-' + price.idPaymentType}
onClick={this.toggle}></i>
</div>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-additonal-' + price.idPaymentType}
className="price-info-popover"
container="shop-package-buy-info"
toggle={this.toggle}>
<PopoverBody>
<div>
{
price.recurentExtra > 0 &&
<div className="package-price-recurrent">
<span className="price-info-title">{coMarketTexts.labels.RECURRENT_PRICE}: </span>
{(price.recurentExtra).toLocaleString()} {country.currency} / {price.periodUnit}
{
price.packagePayPeriod > 0 &&
<span>
{' '} for {price.packagePayPeriod} {price.periodUnit}
</span>
}
</div>
}
{
price.servicesExtra > 0 &&
<div className="services-price-recurrent">
<span className="price-info-title">{coMarketTexts.labels.SERVICE_PRICE}: </span>
{(price.servicesExtra).toLocaleString()} {country.currency} / {price.periodUnit}
{
price.servicesContractPeriod > 0 &&
<span>
{' '} for {price.servicesContractPeriod} {price.periodUnit} {coMarketTexts.labels.EXTEND} {price.periodUnit} (Max {price.maxContractPeriod} {price.periodUnit})
</span>
}
</div>
}
</div>
</PopoverBody>
</Popover>
</Col>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement
});
export default connect(mapStateToProps)(AgreementOptionItem);

View File

@@ -0,0 +1,24 @@
import React, {Component} from 'react';
import {Row} from 'reactstrap';
import AgreementOptionItemContainer from './AgreementOptionItemContainer.jsx';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class AgreementOptions extends Component {
render() {
const {prices, country} = this.props;
return (
<div className="shop-package-options">
<h6 className="shop-package-label options-header">{coMarketTexts.labels.AGREEMENT_OPTIONS}:</h6>
<Row>
{
prices && prices.map((price) => {
return <AgreementOptionItemContainer key={'agreement-' + price.idPaymentType} country={country} price={price}/>
})
}
</Row>
</div>
);
}
}
export default AgreementOptions;

View File

@@ -0,0 +1,20 @@
import React, {Component} from 'react';
class CartIcon extends Component {
render() {
const cartCount = parseInt(this.props.cartCount, 10);
return (
<span className="items-cart-count">
{ cartCount === 0
? <span>Cart is empty</span>
: cartCount === 1
? <span>1 item in cart</span>
: <span>{cartCount} items in cart</span>
}
</span>
);
}
}
export default CartIcon;

View File

@@ -0,0 +1,83 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Select from 'react-select';
import {fetchShopPackages, fetchShopCommercialLeads, selectCommercialLead} from '../../../actions/coMarket/coMarketPackagesActions';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class CoMarketCatalogSelect extends Component {
constructor(props) {
super(props);
this.handleClChange = this.handleClChange.bind(this);
this.handleSearchChange = this.handleSearchChange.bind(this);
this.state = {
searchValue : ''
};
}
componentDidMount() {
this.props.dispatch(fetchShopCommercialLeads());
if(this.props.commercialLeads && this.props.cartItems && this.props.activeModule==='cart'){
const cl = this.props.commercialLeads.find((cl) => {return cl.idCommercialLead===this.props.cartItems[0].idCommercialLead});
this.props.dispatch(selectCommercialLead(cl));
}
}
componentWillReceiveProps(nextProps){
if(nextProps.activeModule==='cart' && nextProps.commercialLeads && nextProps.cartItems && nextProps.cartItems.length > 0){
const cl = nextProps.commercialLeads.find((cl) => {return cl.idCommercialLead===nextProps.cartItems[0].idCommercialLead});
nextProps.dispatch(selectCommercialLead(cl));
}
if(nextProps.commercialLeads && nextProps.idCommercialLead && nextProps.activeModule === 'co-market'){
const cl = nextProps.commercialLeads.find((cl) => {return cl.idCommercialLead===nextProps.idCommercialLead});
nextProps.dispatch(selectCommercialLead(cl));
}
}
handleClChange(cl) {
this.props.dispatch(selectCommercialLead(cl));
this.props.dispatch(fetchShopPackages(cl));
}
handleSearchChange(event) {
this.setState({searchValue: event.target.value});
this.props.dispatch(fetchShopPackages(this.props.selectedCommercialLead, event.target.value));
}
render() {
const {commercialLeads, selectedCommercialLead, idPackage, activeSubmodule} = this.props;
const isDisabled = (idPackage || this.props.activeModule === 'cart') ? true : false;
return (
<div id="co-market-catalog">
{
commercialLeads && activeSubmodule !== 'orders' &&
<div className="filters co-market-nav-div">
<div className="filter-name">{coMarketTexts.labels.CATALOGUE}:</div>
<Select value={selectedCommercialLead}
name="commercialLead"
className="filter-select"
placeholder={coMarketTexts.labels.SELECT_CL}
options={commercialLeads}
disabled={isDisabled}
clearable={false}
onChange={(cl) => {this.handleClChange(cl)}}
/>
</div>
}
</div>
);
}
}
const mapStateToProps = (state) => ({
commercialLeads: state.coMarketPackagesReducer.commercialLeads,
selectedCommercialLead: state.coMarketPackagesReducer.selectedCommercialLead,
idPackage: state.coMarketReducer.idPackage,
cartItems: state.cartReducer.cartItems,
activeSubmodule: state.pageReducer.activeSubmodule
});
export default connect(mapStateToProps)(CoMarketCatalogSelect);

View File

@@ -0,0 +1,61 @@
import React, {Component} from 'react';
import {Col} from 'reactstrap';
import {API_SERVER} from '../../../config';
import FileDownloader from '../../../helpers/FileDownloader';
import {coMarketTexts} from '../../../constants/coMarketConstants';
const fileHandler = new FileDownloader();
class PackageInfo extends Component {
downloadDocument(document){
const fileUrl = `${API_SERVER}/utils/api/downloadFile?idDocument=${document.idDocument}&fileName=${document.documentName}.${document.extension}`
const fileName = document.documentName + '.' + document.extension;
fileHandler.download(fileUrl, fileName);
}
render() {
const {packageInfo, documents} = this.props;
return (
<div id="shop-package-general-info" className="shop-package-general-info">
{ packageInfo &&
<span>
<Col lg="12"
className="shop-package-title"><h4>{packageInfo.name}</h4></Col>
<Col lg="12"
className="shop-package-reference"><h6>{packageInfo.reference}</h6></Col>
<Col lg="12"
dangerouslySetInnerHTML={{__html: packageInfo.shortDescription}}
className="shop-package-full-description"></Col>
<Col lg="12"
className="shop-package-details-country">
<div className="shop-package-label">{coMarketTexts.labels.AVAILABLE_IN}:</div>
<div className="shop-package-text">
{packageInfo.country} <span className={'flag-icon flag-icon-' + packageInfo.countryCode}></span>
</div>
</Col>
</span>
}
{
documents && documents.length > 0 &&
<Col lg="12"
className="shop-package-details-documents">
<div className="shop-package-label">{coMarketTexts.labels.DOCUMENTS}:</div>
{
documents.map((document) =>
<div key={document.idDocument}>
<span className="document-link"
onClick={() => {this.downloadDocument(document)}}>
<i className="fa fa-file" aria-hidden="true"></i> {document.documentName} ({document.extension})
</span>
</div>
)
}
</Col>
}
</div>
);
}
}
export default PackageInfo;

View File

@@ -0,0 +1,123 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col} from 'reactstrap';
import {selectOption} from '../../../actions/coMarket/coMarketPackageDetailsActions';
import PriceHelper from '../../../helpers/coMarket/PriceHelper';
import {coMarketTexts} from '../../../constants/coMarketConstants';
const priceHelper = new PriceHelper();
class PackageOptionItem extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.handleOptionChange = this.handleOptionChange.bind(this);
this.state = {
popoverOpen: false
};
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
handleOptionChange() {
this.props.dispatch(selectOption(this.props.idGroup, this.props.option));
}
isChecked() {
return this.props.selectedOptions
&& this.props.selectedOptions[this.props.idGroup]
&& this.props.selectedOptions[this.props.idGroup].idOptionPackage === this.props.option.idOptionPackage;
}
getClass(isChecked) {
return isChecked ?
'selected-option' :
'';
}
formatName(name) {
return name.length > 39 ? name.substring(0, 40) + '...' : name;
}
render() {
const {idGroup, option, country} = this.props;
const selectedPrice = this.props.selectedAgreement ? priceHelper.getSelectedPrice(option, this.props.selectedAgreement) : null;
const isChecked = this.isChecked();
return (
<Col xl="4" lg="4" md="6" xs="6" className="option-value">
<div className={'form-check-inline option-selection ' + this.getClass(isChecked)}>
<Label check>
<Input type="radio"
name={'package-option-'+ idGroup}
className="package-option-input"
onChange={this.handleOptionChange}
checked={isChecked}
value={option.idOptionPackage}/>
<div className="option-name">{this.formatName(option.optionName)}</div>
<div className="option-description" dangerouslySetInnerHTML={{__html: option.shortDescription}}></div>
{
priceHelper.hasFixedPrice(selectedPrice) &&
<div className="option-prices">
<table className="price-table option-price-table">
<thead>
<tr>
<th>{coMarketTexts.labels.ON_DELIVERY}</th>
<th>{coMarketTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{selectedPrice.fixedExtra && selectedPrice.fixedExtra.toLocaleString() + ' ' + country.currency} </td>
<td>
{
priceHelper.hasRecurrentPrice(selectedPrice) ?
<div className="option-value-text monthly-price">
{priceHelper.sumPrices([selectedPrice.recurentExtra, selectedPrice.servicesExtra]).toLocaleString()} {country.currency}
</div>
: <div className="option-value-text monthly-price"> 0 </div>
}
</td>
</tr>
</tbody>
</table>
</div>
}
</Label>
{
!selectedPrice &&
<Label>
<div className="not-available">
({coMarketTexts.labels.NOT_AVAILABLE})
<i className="price-info-btn fa fa-info-circle"
aria-hidden="true"
id={'info-option-' + idGroup + '-' + option.idOptionPackage}
onClick={this.toggle}></i>
</div>
</Label>
}
</div>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-option-' + idGroup + '-' + option.idOptionPackage}
toggle={this.toggle}>
<PopoverBody>
{coMarketTexts.labels.SELECTION_NOT_AVAILABLE}
</PopoverBody>
</Popover>
</Col>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedOptions: state.coMarketPackageDetailsReducer.selectedOptions
});
export default connect(mapStateToProps)(PackageOptionItem);

View File

@@ -0,0 +1,35 @@
import React, {Component} from 'react';
import {Row} from 'reactstrap';
import PackageOptionItemContainer from './PackageOptionItemContainer.jsx';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class PackageOptions extends Component {
render() {
const {groups, country} = this.props;
return (
<div className="shop-package-options">
<h6 className="shop-package-label options-header">{coMarketTexts.labels.PACKAGE_OPTIONS}</h6>
{
Object.keys(groups).map(groupKey => {
const group = groups[groupKey];
return <div key={'group-' + group.idGroup} className="package-option">
<h6 className="shop-package-label"> {group.groupName}:</h6>
<Row>
{
group.options.map(option => {
return <PackageOptionItemContainer key={'option-' + group.idGroup + option.idOptionPackage}
option={option}
country={country}
idGroup={group.idGroup}/>
})
}
</Row>
</div>
})
}
</div>
);
}
}
export default PackageOptions;

View File

@@ -0,0 +1,80 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class PackagePrice extends Component {
constructor(props) {
super(props);
this.getFinalPrice = this.getFinalPrice.bind(this);
this.filterByAgreement = this.filterByAgreement.bind(this);
}
filterByAgreement(price) {
return price.idPaymentType === this.props.selectedAgreement.idPaymentType;
}
getExtra(selected, priceType) {
const extraPriceObj = selected.prices.find(this.filterByAgreement);
const extraPrice = extraPriceObj ? extraPriceObj[priceType] : 0;
return extraPrice;
}
getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, priceType) {
let price = selectedAgreement ? selectedAgreement[priceType] : 0;
if(selectedAgreement && selectedOptions) {
Object.keys(selectedOptions).forEach((idGroup) => {
price += this.getExtra(selectedOptions[idGroup], priceType);
});
}
if(selectedAgreement && selectedAdditionals) {
selectedAdditionals.forEach((additional) => {
price += this.getExtra(additional, priceType);
});
}
return price;
}
render() {
const {country, selectedAgreement, selectedOptions, selectedAdditionals} = this.props;
return (
<div className="selection-price">
{
selectedAgreement &&
<table className="price-table main-price">
<thead>
<tr>
<th>{coMarketTexts.labels.ON_DELIVERY}</th>
<th>{coMarketTexts.labels.MONTHLY}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'fixedExtra').toLocaleString()} {country.currency}
</td>
<td>
{(this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'recurentExtra')
+ this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'servicesExtra')).toLocaleString()}
{' '}{country.currency}
</td>
</tr>
</tbody>
</table>
}
</div>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedOptions: state.coMarketPackageDetailsReducer.selectedOptions,
selectedAdditionals: state.coMarketPackageDetailsReducer.selectedAdditionals
});
export default connect(mapStateToProps)(PackagePrice);

View File

@@ -0,0 +1,45 @@
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import { Col, Card, CardBody, CardTitle, CardSubtitle, Button} from 'reactstrap';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class ShopItem extends Component {
getShopItemPackageTitle(name) {
return name.length > 40 ? name.substring(0, 40) + '...' : name;
}
render() {
const {shopPackage, idCommercialLead} = this.props;
return (
<Col xl="3" lg="4" md="6" sm="12" xs="12">
<Card className="shop-package-item">
<div className="shop-image-photo" style={{'background-image': 'url("'+(shopPackage.photo || 'static/img/no-photo-package.jpg') +'")'}}></div>
<CardBody>
<CardTitle className="shop-package-title">
<Link to={`/co-market/${idCommercialLead}/${shopPackage.idPackage}`}>
{this.getShopItemPackageTitle(shopPackage.name)}
</Link>
</CardTitle>
<CardSubtitle className="shop-package-reference">
{shopPackage.reference}
</CardSubtitle>
<div className="shop-package-country">
{coMarketTexts.labels.AVAILABLE_IN}: {shopPackage.country}
<span className={'flag-icon flag-icon-' + shopPackage.countryCode}></span>
</div>
<div className="shop-package-details-btn-layer">
<Link id={'shop-package-details-' + shopPackage.idPackage} to={`/co-market/${idCommercialLead}/${shopPackage.idPackage}`}>
<Button className="shop-package-details-btn">{coMarketTexts.buttons.DETAILS}</Button>
</Link>
</div>
</CardBody>
</Card>
</Col>
);
}
}
export default ShopItem;

View File

@@ -0,0 +1,175 @@
@import '../../../styleConstants.scss';
#co-market-shop {
.shop-package-title {
font-size: 1rem;
height: 2rem;
}
.shop-package-title a {
font-size: $font-size-msmall;
font-weight: $font-weight;
text-align: left;
color: $header-background;
}
.shop-package-reference {
font-size: $font-size-xsmal;
font-weight: $font-weight;
text-align: left;
color: $warmGreyColor;
}
.shop-package-country {
display: inline-block;
width: 50%;
font-size: $font-size-xsmal;
text-align: left;
color: $warmGreyColor;
}
.shop-package-details-btn-layer{
display: inline-block;
width: 50%;
text-align: right;
}
.shop-package-details-btn{
border-radius: 50px;
background-color: $whiteColor;
border: solid 0.7px $borderColor;
color: $darkBlue;
font-weight: $font-weight;
font-size: $font-size-xsmal;
cursor: pointer;
}
.shop-package-item{
margin-top: 1rem;
border-radius: 4px;
background-color: $whiteColor;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
}
.flag-icon{
border-radius: 2px;
margin-left: 0.2rem;
}
.co-market-nav {
font-size: $font-size-msmall;
text-align: left;
color: $warmGreyColor;
vertical-align: middle;
}
.co-market-nav-buttons{
display: flex;
align-items: center;
color: $header-background;
font-weight: $font-weight;
}
.search-layer{
display:flex;
align-items:center;
}
.search-layer div{
position: relative;
}
.wiaas-input {
border-radius: 3.125rem;
background-color: $whiteColor;
border: solid 1px $borderColor;
padding: 0.2rem 1.4rem;
font-size: $font-size-xsmal;
font-weight: 300;
text-align: left;
color: $warmGreyColor;
}
input:focus {
outline: none;
}
.search-package-icon {
position: absolute;
font-size: $font-size-xsmal;
left: 0.4rem;
top: 0.3rem;
color: $warmGreyColor;
}
.shop-image-photo{
width:100%;
height:129px;
background-size: cover;
}
}
#co-market-big-commercial{
background: $whiteColor;
text-align: center;
.ricoh-text {
color: $ricohRed;
}
.description-photo{
max-width: 100%;
}
}
#co-market-commercials {
background: $whiteColor;
.commercial-photo {
width: 100%;
}
}
#co-market-catalog {
.filter-name {
margin-top: 0.4rem;
font-size: $font-size-normal;
display: inline-block;
margin-right: 0.2rem;
font-weight: $font-weight;
}
.filter-select{
display: inline-block;
width: 21rem;
vertical-align: top;
}
}
@media all and (max-width: 767px) {
.co-market-nav-div {
width: 100%;
text-align: center;
margin: 0.5rem 0;
font-size: $font-size-normal;
}
.wiaas-input {
font-size: $font-size-normal;
}
#co-market-shop {
.shop-package-title a {
font-size: $font-size-big;
}
.shop-package-reference, .shop-package-country{
font-size: $font-size-normal;
}
.shop-package-details-btn{
font-size: $font-size-xbig;
}
}
}

View File

@@ -0,0 +1,159 @@
@import '../../../styleConstants.scss';
#co-market-package-details{
background: $whiteColor;
border-radius: $box-radius;
}
#shop-package-general-info {
padding: 2rem;
.shop-package-title {
font-size: $font-size-big;
text-align: center;
font-weight: $font-weight;
}
.shop-package-reference {
margin-bottom: 5%;
text-align: center;
font-weight: $font-weight;
font-size: $font-size-normal;
color: $darkGreyColor;
}
.shop-package-full-description {
font-size: $font-size-normal;
overflow: auto;
min-height: 15rem;
max-height: 30rem;
}
.shop-package-label{
display: inline-block;
}
.shop-package-text {
display: inline-block;
margin-left: 1rem;
}
.shop-package-details-country{
margin-top: 1rem;
}
.shop-package-details-documents{
margin-top: 1rem;
}
.document-link{
color: #33425b;
cursor: pointer;
}
}
#shop-package-buy-info{
padding: 2rem;
.shop-package-options{
margin: 1rem 0;
border-bottom: 2px solid $title-color;
}
.not-available {
margin-top: 1rem;
margin-left: 0.2rem;
color: $not-available-status-color;
font-weight: $font-weight;
font-size: $font-size-msmall;
}
.price-info-btn {
color: $info-color;
cursor: pointer;
margin-left: 0.2rem;
}
.add-to-cart-btn {
cursor: pointer;
color: $darkGreyColor;
background-color: $whiteColor;
border-color:$warmGreyColor;
}
.selected-option {
font-weight: $font-weight;
background: $hoverColor;
border-radius: $box-radius;
}
.option-selection {
display: inline-block;
vertical-align: top;
padding: 0.5rem;
}
.option-selection:hover {
background: $footer-background;
color: $whiteColor;
border-radius: $box-radius;
}
.option-selection:hover .price-info-btn {
color: $whiteColor;
}
.option-name {
font-size: $font-size-msmall;
height: 2rem;
}
.option-description {
padding-top: 0.5rem;
font-size: $font-size-msmall;
height: 2rem;
}
.price-info-popover {
max-width: 50rem;
}
.recurent-total-price{
font-size: $font-size-msmall;
}
.selection-price{
background: $hoverColor;
display: inline-block;
border-radius: $box-radius;
font-size: $font-size-big;
font-weight: bold;
}
.option-prices {
margin-top: 0.5rem;
border-radius: $box-radius;
}
.price-table {
width: 8rem;
}
.main-price {
width: auto;
}
.price-table th{
padding: 0.5rem;
}
.price-table td{
border-top: 1px solid $darkGreyColor;
padding: 0.5rem;
}
.option-price-table{
font-size: $font-size-xsmal;
}
}

View File

@@ -0,0 +1,42 @@
@import '../styleConstants.scss';
.App-header {
min-height: 7.2rem;
}
.wiaas-button{
background-color: $whiteColor;
color: $darkGreyColor;
font-size: $font-size-small;
cursor: pointer;
}
.main-wrapper {
position: relative;
padding: 7.5rem 0 5rem 0;
}
.loader{
padding-top: 1rem;
text-align: center;
}
.schedule-installation-btn {
cursor: pointer;
background-color: $whiteColor;
color: $darkGreyColor;
border: 0.1rem $border-grey solid;
display: inline-block;
border-radius: $box-radius;
font-weight: $font-weight;
}
@media all and (max-width: 992px) {
.main-wrapper {
padding: 0 0 5rem 0;
}
.app-content{
min-height: 400px;
}
}

View File

@@ -0,0 +1,19 @@
@import '../../styleConstants.scss';
.order-placed-message {
padding: 0.5rem 1rem;
}
#dashboard-container {
.dashborad-message {
padding: 2rem;
background: $whiteColor;
color: $darkGreyColor;
font-weight: $font-weight;
}
.dashborad-message a {
color: $darkGreyColor;
font-weight: $font-weight;
}
}

View File

@@ -0,0 +1,83 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Container, Row, Alert, Col} from 'reactstrap';
import OrderCentralContainer from './OrderCentralContainer.jsx';
import NextActionsContainer from './NextActionsContainer.jsx';
import {fetchGadgets} from '../../actions/dashboard/dashboardActions';
import {setOrderPlacedFlag, setOrderPlacedRedirectFlag} from '../../actions/cart/cartActions';
import {dashboardTexts} from '../../constants/dashboardConstants';
import './Dashboards.css';
const gadgetContainers = {
CustomerOrderCentral : OrderCentralContainer,
CustomerNextActions: NextActionsContainer
}
class DashboardsContainer extends Component {
constructor(props) {
super(props);
this.state = {
orderPlaced: this.props.orderPlaced || false,
redirect: this.props.orderPlacedRedirect || true
};
}
componentDidMount() {
this.props.dispatch(fetchGadgets());
if(this.state.orderPlaced && this.state.redirect) {
this.setState({redirect: false});
this.props.dispatch(setOrderPlacedRedirectFlag(false));
}
}
render() {
const {gadgets} = this.props;
if(this.state.orderPlaced) {
setTimeout(() => {
this.setState({orderPlaced: false});
this.props.dispatch(setOrderPlacedFlag(false));
}, 15000);
}
return (
<div id="dashboard-container">
<Container fluid={true} id="dashborad-gadgets">
<Row>
{
this.state.orderPlaced &&
<Col>
<Alert color="success" className="order-placed-message">
{dashboardTexts.messages.ORDER_PLACED}
</Alert>
</Col>
}
</Row>
<Row>
{
gadgets &&
gadgets.map((gadget, Key) => {
const containerKey = gadget.name.replace(/\s/g, '');
if(gadgetContainers[containerKey]){
const TagName = gadgetContainers[containerKey];
return <TagName key={gadget.idGadget} gadget={gadget}/>;
}
return '';
})
}
</Row>
</Container>
</div>
);
}
}
const mapStateToProps = (state) => ({
gadgets: state.dashboardReducer.gadgets,
orderPlaced: state.cartReducer.orderPlaced,
orderPlacedRedirect: state.cartReducer.orderPlacedRedirect
});
export default connect(mapStateToProps)(DashboardsContainer);

View File

@@ -0,0 +1,46 @@
@import '../../styleConstants.scss';
#next-actions-gadget{
.next-actions-row {
font-size: $font-size-small;
font-weight: $font-weight;
text-align: left;
color: $darkGreyColor;
padding: 1rem 0;
}
.next-actions-status {
display: inline-block;
border-radius: $box-radius;
margin-left: 0.5rem;
font-size: $font-size-xsmal;
padding: 0.1rem 0.3rem;
color: $whiteColor;
cursor: pointer;
border: 1px solid $whiteColor;
}
.next-actions-status:hover{
border: 1px solid $darkGreyColor;
}
.invalid {
background: $invalid-status-color;
}
.pending {
background: $pending-status-color;
}
.not-accepted {
background: $not-accepted-status-color;
}
.in-progress {
background: $in-progress-status-color;
}
.next-action-details{
float: right;
}
}

View File

@@ -0,0 +1,41 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Col} from 'reactstrap';
import {fetchNextActions} from '../../actions/dashboard/nextActionsActions';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import NextActionsList from './components/NextActionsList.jsx';
import {dashboardTexts} from '../../constants/dashboardConstants';
import './NextActions.css';
class NextActionsContainer extends Component {
componentDidMount() {
this.props.dispatch(fetchNextActions());
}
render() {
const {nextActions, isLoading} = this.props;
return (<Col xl="4" lg="4" md="12" sm="12" xs="12">
<WiaasBox id="next-actions-gadget" mainTitle={dashboardTexts.labels.NEXT_ACTIONS}>
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(nextActions && nextActions.length > 0 && !isLoading) ?
<NextActionsList nextActions={nextActions}/> :
<div className="dashborad-message">{dashboardTexts.labels.NO_ACTIONS}</div>
}
</WiaasBox>
</Col>);
}
}
const mapStateToProps = (state) => ({
nextActions: state.nextActionsReducer.nextActions,
isLoading: state.nextActionsReducer.isLoading
});
export default connect(mapStateToProps)(NextActionsContainer);

View File

@@ -0,0 +1,45 @@
@import '../../styleConstants.scss';
#order-central-gadget {
.order-central-row {
border-radius: 1.5px;
}
.package-photo{
margin-right: 0.5rem;
}
.orderNumber{
display: inline-block;
bottom: 0;
font-size: $font-size-msmall;
a {
color: $darkGreyColor;
}
}
.status-icon {
width: $font-size-xsmal;
height: $font-size-xsmal;
display: inline-block;
border-radius: 50%;
margin-right: 0.5rem;
}
.open {
background: $open-status-color;
}
.in-progress {
background: $in-progress-status-color;
}
.line-open {
border-left: 3px $open-status-color solid;
}
.line-in-progress {
border-left: 3px $in-progress-status-color solid;
}
}

View File

@@ -0,0 +1,69 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Col} from 'reactstrap';
import {Link} from 'react-router-dom';
import {fetchOrders} from '../../actions/dashboard/ordersActions';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import OrdersList from './components/OrdersList.jsx';
import OrderListHeader from '../orders/components/OrderListHeader.jsx';
import {dashboardTexts} from '../../constants/dashboardConstants';
import './OrderCentral.css';
const type = 'overview';
class OrderCentralContainer extends Component {
constructor(props) {
super(props);
this.state = {
orders: [],
isViewAllOrdersChecked: false
};
}
componentDidMount() {
this.props.dispatch(fetchOrders());
}
componentWillReceiveProps(nextProps) {
if(nextProps.isViewAllOrdersChecked[type] !== this.state.isViewAllOrdersChecked) {
this.setState({
isViewAllOrdersChecked: nextProps.isViewAllOrdersChecked[type] || false
});
this.props.dispatch(fetchOrders(nextProps.isViewAllOrdersChecked[type]));
}
this.setState({
orders: nextProps.orders
});
}
render() {
const {isLoading} = this.props;
return (<Col xl="8" lg="8" md="12" sm="12" xs="12">
<WiaasBox id="order-central-gadget" mainTitle={dashboardTexts.labels.ORDER_CENTRAL} customHeader={OrderListHeader} customHeaderParams={type}>
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(this.state.orders && this.state.orders.length > 0 && !isLoading) ?
<OrdersList orders={this.state.orders} isViewAllOrdersChecked={this.props.isViewAllOrdersChecked[type]}/> :
<div className="dashborad-message">
<Link to='/co-market'>{dashboardTexts.labels.NO_ORDERS}</Link>
</div>
}
</WiaasBox>
</Col>);
}
}
const mapStateToProps = (state) => ({
orders: state.ordersCentralReducer.orders,
isLoading: state.ordersCentralReducer.isLoading,
isViewAllOrdersChecked: state.ordersReducer.isViewAllOrdersChecked
});
export default connect(mapStateToProps)(OrderCentralContainer);

View File

@@ -0,0 +1,25 @@
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import {Col, Row} from 'reactstrap';
import {dashboardTexts} from '../../../constants/dashboardConstants';
class NextActionItem extends Component {
render() {
const {nextAction} = this.props;
return (
<Row className="next-actions-row">
<Col xl="8">
{nextAction.stepAction}
</Col>
<Col xl="4">
<Link to={'orders/'+ nextAction.idOrder}>
<div className={'next-actions-status ' + nextAction.status}>{dashboardTexts.statuses[nextAction.status]}</div>
</Link>
</Col>
</Row>
);
}
}
export default NextActionItem;

View File

@@ -0,0 +1,27 @@
import React, {Component} from 'react';
import NextActionItem from './NextActionItem';
import {WiaasTable, WiaasTableBody} from '../../../mainComponents/table/WiaasTable.jsx';
class NextActionsList extends Component {
render() {
const {nextActions} = this.props;
return (
<div>
<WiaasTable>
<WiaasTableBody>
{
nextActions &&
nextActions.map((nextAction, index) =>
<NextActionItem key={'action-' + nextAction.orderNumber + index} nextAction={nextAction}/>
)
}
</WiaasTableBody>
</WiaasTable>
</div>
);
}
}
export default NextActionsList;

View File

@@ -0,0 +1,34 @@
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
import {Button} from 'reactstrap';
import { WiaasTableRow, WiaasTableCol} from '../../../mainComponents/table/WiaasTable.jsx';
import {dashboardTexts} from '../../../constants/dashboardConstants';
class OrderItem extends Component {
render() {
const {order, isViewAllOrdersChecked} = this.props;
return (
<WiaasTableRow className={'order-central-row line-' + order.status}>
<WiaasTableCol header="#">
<i className="fa fa-list-alt package-photo" aria-hidden="true" />
<div className="orderNumber">{order.orderNumber}</div>
</WiaasTableCol>
<WiaasTableCol header={dashboardTexts.tableHeaders.ORDER_DATE}>{order.orderDate}</WiaasTableCol>
{
isViewAllOrdersChecked &&
<WiaasTableCol header={dashboardTexts.tableHeaders.PLACED_BY}>{order.placedBy}</WiaasTableCol>
}
<WiaasTableCol header={dashboardTexts.tableHeaders.REFERENCE}>{order.reference}</WiaasTableCol>
<WiaasTableCol header={dashboardTexts.tableHeaders.ON_DELIVERY}>{order.fixedPrice.toLocaleString()} {order.currency}</WiaasTableCol>
<WiaasTableCol header={dashboardTexts.tableHeaders.MONTHLY}>{order.recurringPrice.toLocaleString()} {order.currency}</WiaasTableCol>
<WiaasTableCol header={dashboardTexts.tableHeaders.STATUS}>
<div className={'status-icon ' + order.status}></div>{dashboardTexts.statuses[order.status]}
</WiaasTableCol>
<WiaasTableCol><Link to={'orders/'+ order.idOrder}><Button className="wiaas-button">{dashboardTexts.buttons.DETAILS}</Button></Link></WiaasTableCol>
</WiaasTableRow>
);
}
}
export default OrderItem;

View File

@@ -0,0 +1,59 @@
import React, {Component} from 'react';
import OrderItem from './OrderItem';
import {WiaasTable, WiaasTableHeader, WiaasTableBody} from '../../../mainComponents/table/WiaasTable.jsx';
import {dashboardTexts} from '../../../constants/dashboardConstants';
class OrdersList extends Component {
constructor(props) {
super(props);
this.state = {
isViewAllOrdersChecked: this.props.isViewAllOrdersChecked || false
};
this.getHeaderForOrders = this.getHeaderForOrders.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({isViewAllOrdersChecked: nextProps.isViewAllOrdersChecked});
}
getHeaderForOrders() {
const headers = [
dashboardTexts.tableHeaders.ORDER,
dashboardTexts.tableHeaders.ORDER_DATE,
dashboardTexts.tableHeaders.REFERENCE,
dashboardTexts.tableHeaders.ON_DELIVERY,
dashboardTexts.tableHeaders.MONTHLY,
dashboardTexts.tableHeaders.STATUS,
''
];
if(this.state.isViewAllOrdersChecked) {
headers.splice(2, 0, dashboardTexts.tableHeaders.PLACED_BY);
headers.join();
}
return headers;
}
render() {
const {orders, isViewAllOrdersChecked} = this.props;
return (
<div>
<WiaasTable>
<WiaasTableHeader headers={this.getHeaderForOrders()}/>
<WiaasTableBody>
{
orders &&
orders.map((order, index) =>
<OrderItem key={'order-' + order.orderNumber} order={order} isViewAllOrdersChecked={isViewAllOrdersChecked}/>
)
}
</WiaasTableBody>
</WiaasTable>
</div>
);
}
}
export default OrdersList;

View File

@@ -0,0 +1,17 @@
import React, {Component} from 'react';
import TermsContainer from '../terms/TermsContainer.jsx';
import './style/Footer.css';
class Footer extends Component {
render() {
return (
<footer className="App-footer">
<div className="footer-buttons">
<TermsContainer />
</div>
</footer>
);
}
}
export default Footer;

View File

@@ -0,0 +1,12 @@
@import '../../../styleConstants.scss';
footer {
.footer-btn {
cursor: pointer;
color: $whiteColor;
}
}
.modal-dialog{
max-width: 80%;
}

View File

@@ -0,0 +1,90 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Link, Redirect} from 'react-router-dom';
import {
Alert,
Form,
FormGroup,
Label,
Input,
Button
} from 'reactstrap';
import {changePassword} from '../../actions/login/authActions';
import {loginMessages, loginTexts} from '../../constants/authConstants';
class ChangePasswordContainer extends Component {
constructor(props) {
super(props);
this.state = {
credentials: {
password: '',
repassword: ''
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const field = event.target.name;
const credentials = this.state.credentials;
credentials[field] = event.target.value;
return this.setState({credentials: credentials});
}
handleSubmit(event) {
const token = this.props.match && this.props.match.params ? this.props.match.params.token : '';
event.preventDefault();
this.props.dispatch(changePassword(token, this.state.credentials.password, this.state.credentials.repassword));
}
render() {
const {isPasswordChanged} = this.props;
return (<div className="chnage-password">
{
isPasswordChanged && <Redirect push to="/"/>
}
<Form onSubmit={this.handleSubmit}>
<FormGroup className="sign-in">{loginTexts.labels.CHANGE_PASSWORD}</FormGroup>
<FormGroup className="login-input-group">
<Label className="login-input-text" for="password">{loginTexts.labels.NEW_PASSWORD}</Label>
<Input className="base" type="password" name="password" id="password" placeholder={loginTexts.labels.NEW_PASSWORD} value={this.state.password} onChange={this.handleChange} autoFocus />
</FormGroup>
<FormGroup className="login-input-group">
<Label className="login-input-text" for="repassword">{loginTexts.labels.CONFIRM_PASSWORD}</Label>
<Input className="base" type="password" name="repassword" id="repassword" placeholder={loginTexts.labels.CONFIRM_PASSWORD} value={this.state.repassword} onChange={this.handleChange} />
</FormGroup>
<FormGroup className="login-form-group">
<Button className="base-sign-in" type="submit" id="change-button">{loginTexts.buttons.CHANGE_PASSWORD}</Button>
</FormGroup>
<FormGroup className="login-form-group" id="login-message">
{loginMessages[this.props.errorMessage] && <Alert className='alert-form wiaas-alert' color={this.props.messageColor}>{loginMessages[this.props.errorMessage]}</Alert>}
</FormGroup>
<FormGroup className="login-input-group">
<hr className="divider"/>
</FormGroup>
<FormGroup className="login-input-group">
<Link to={'/'}>
<Button className="forgot-password">{loginTexts.buttons.BACK_TO_SIGNIN}</Button>
</Link>
</FormGroup>
</Form>
</div>);
}
}
const mapStateToProps = (state) => ({
errorMessage: state.auth.errorMessage,
messageColor: state.auth.messageColor,
isPasswordChanged: state.auth.isPasswordChanged
});
export default connect(mapStateToProps)(ChangePasswordContainer);

View File

@@ -0,0 +1,91 @@
import React, {Component} from 'react';
import {Switch, Route} from 'react-router-dom';
import Particles from 'react-particles-js';
import {BrowserRouter} from 'react-router-dom';
import logoDefault from '../../svg/logoDefault.svg';
import {
Row,
Card,
CardImg,
CardText,
CardBody,
Container,
Col,
CardTitle
} from 'reactstrap';
import './login.css';
import {APPLICATION_NAME} from '../../config';
import LogInForm from './LogInForm.jsx';
import ChangePasswordContainer from './ChangePasswordContainer.jsx';
import {loginTexts} from '../../constants/authConstants';
const paramsForParticles = {
particles: {
number: {
value: 110,
density: {
enable: true,
value_area: 2500
}
},
line_linked: {
enable: true,
distance: 300,
color: "#ffffff",
opacity: 1.5,
width: 3
},
"shape": {
"type": "circle",
"stroke": {
"width": 10,
"height": 100,
"color": "white"
},
polygon: {
nb_sides: 6
}
}
}
}
class LogInContainer extends Component {
render() {
return (
<BrowserRouter>
<Container fluid={true} className="main-col">
<Col className="background-allPage" >
<Col className="background-left"></Col>
<Col className="background-right">
<Particles className="particles background-right w3-display-topright" params={paramsForParticles}/>
</Col>
<Row className="form-login">
<Col md={{ size: 4, offset: 2 }} xs="12" sm="12" className="login-intro">
<Card className="border-card-color">
<CardImg className="icon-logo-v2" src={logoDefault} alt="Wiaas logo"/>
<CardBody className="introduction">
<CardTitle className="welcome-to-WIAAS">
<span >{loginTexts.labels.WELCOME_TO}</span>{' '}
<span className="text-style-1">{APPLICATION_NAME}</span>
</CardTitle>
<CardText className="welcome-text"></CardText>
</CardBody>
</Card>
<hr className="divider login-separator"/>
</Col>
<Col md="3" xs="12" sm="12" className="form-BG">
<Switch>
<Route exact path='/changePassword/:token' component={ChangePasswordContainer} />
<Route path='/' component={LogInForm} />
</Switch>
</Col>
</Row>
</Col>
</Container>
</BrowserRouter>);
}
}
export default LogInContainer;

View File

@@ -0,0 +1,128 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
Alert,
Form,
FormGroup,
Label,
Input,
Button
} from 'reactstrap';
import './login.css';
import {validateCredentials, validateAccessToken, generatePassword} from '../../actions/login/authActions';
import {setDialogContent, setDialogOpenFlag} from '../../actions/dialog/dialogActions';
import {loginMessages, loginTexts} from '../../constants/authConstants';
class LogInForm extends Component {
constructor(props) {
super(props);
this.state = {
credentials: {
username: '',
password: ''
},
modal: false,
email: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.toggle = this.toggle.bind(this);
this.handlePwdGenerator = this.handlePwdGenerator.bind(this);
}
toggle() {
this.setState({
modal: !this.state.modal
});
}
handleChange(event) {
const field = event.target.name;
const credentials = this.state.credentials;
credentials[field] = event.target.value;
return this.setState({credentials: credentials});
}
handleSubmit(event) {
this.props.dispatch(validateCredentials(this.state.credentials.username, this.state.credentials.password));
event.preventDefault();
}
handlePwdGenerator(currentDialogState) {
this.props.dispatch(generatePassword(currentDialogState.inputValue));
}
componentDidMount() {
if (localStorage.accessToken) {
this.props.dispatch(validateAccessToken(localStorage.accessToken));
}
}
setDialogParams(dialogContent) {
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
render() {
const dialogContent = {
buttons: [
{
color:'success',
action: this.handlePwdGenerator,
name: 'Recover password',
id: 'recover-password-confirmation'
}, {
color:'secondary',
name: 'Cancel',
id: 'cancel-recover-password-item'
}
],
header: 'Recover password',
body: 'Please enter your email address for password generation',
inputArray: [
{
class: 'base',
type: 'email',
placeholder: 'your@email.com'
}
]
};
return (
<Form onSubmit={this.handleSubmit}>
<FormGroup className="sign-in">{loginTexts.labels.SIGN_IN}</FormGroup>
<FormGroup className="login-input-group">
<Label className="login-input-text" for="username">{loginTexts.labels.USERNAME}</Label>
<Input className="base" type="text" name="username" id="username" placeholder={loginTexts.labels.USERNAME} value={this.state.username} onChange={this.handleChange} autoFocus />
</FormGroup>
<FormGroup className="login-input-group">
<Label className="login-input-text" for="password">{loginTexts.labels.PASSWORD}</Label>
<Input className="base" type="password" name="password" id="password" placeholder={loginTexts.labels.PASSWORD} value={this.state.password} onChange={this.handleChange}/>
</FormGroup>
<FormGroup className="login-form-group">
<Button className="base-sign-in" type="submit" id="login-button" >{loginTexts.buttons.SIGN_IN}</Button>
</FormGroup>
<FormGroup className="login-form-group" id="login-message">
{loginMessages[this.props.errorMessage] && <Alert className='alert-form wiaas-alert' color={this.props.messageColor}>{loginMessages[this.props.errorMessage]}</Alert>}
</FormGroup>
<FormGroup className="login-input-group">
<hr className="divider"/>
</FormGroup>
<FormGroup className="login-input-group">
<Button className="forgot-password" id="forgot-password-button" onClick={() => this.setDialogParams(dialogContent)}>
{loginTexts.buttons.FORGOT_PASSWORD}
</Button>
</FormGroup>
</Form>);
}
}
const mapStateToProps = (state) => ({errorMessage: state.auth.errorMessage, messageColor: state.auth.messageColor});
export default connect(mapStateToProps)(LogInForm);

View File

@@ -0,0 +1,208 @@
@import "../../styleConstants.scss";
header {
margin-bottom: 1rem;
}
#form-sign {
color: red;
}
.my-label {
color: red;
}
.introduction {
margin: auto;
object-fit: contain;
text-align: -webkit-center;
}
.welcome-to-WIAAS {
font-size: 2rem;
color: $header-background;
font-weight: 500;
}
.welcome-to-WIAAS .text-style-1 {
font-weight: $font-weight;
font-size: 2rem;
}
.welcome-text {
opacity: 0.9;
font-size: $font-size-big;
text-align: left;
color: $warmGreyColor;
}
.back-to-login{
color: $warmGreyColor;
}
.login {
height: 100%;
background-color: $whiteColor;
}
.background-allPage {
height: 100%;
background-image: linear-gradient(to bottom, $footer-background , $bottom-background);
overflow: overlay;
}
.background-left {
width: 50%;
height: 100%;
position: fixed;
left: 0;
background-color: $whiteColor;
}
.background-right {
position: fixed;
width: 69.375%;
opacity: 0.2;
right: 0;
height: 100%;
}
.main-col {
height: 100%;
padding-right: unset;
}
.form-login {
top: 32%;
position: relative;
z-index: 0;
}
.login-intro {
width: 58.5%;
height: -webkit-stretch;
background: $whiteColor;
border: none;
border-radius: 0;
}
.icon-logo-v2 {
margin: auto;
width: 53%;
margin-top: 7%;
}
.form-BG {
background-color: $whiteColor;
box-shadow: 0.25rem 0.125rem 0.25rem 0 $shadow-color;
border-radius: 0 0.4375rem 0.4375rem 0;
}
.sign-in {
text-align: center;
font-size: 1.5rem;
color: $darkGreyColor;
margin-top: 7%;
}
.login-input-group {
text-align: left;
margin-right: 5%;
margin-left: 5%;
margin-bottom: 5%;
}
.login-input-text {
font-size: $font-size-small;
font-weight: $font-weight;
text-align: left;
color: $warmGreyColor;
}
.base {
border-radius: 0.25rem;
background-color: $whiteColor;
border: solid 0.0625rem $border-grey;
}
.base-sign-in {
width: 100%;
border-radius: 0.25rem;
background-color: $greenColor;
border-color: $greenColor;
cursor: pointer;
}
.divider {
width: 100%;
height: 0.0625rem;
background-color: $borderColor;
}
.forgot-password {
width: 100%;
height: 2.25rem;
border-radius: 0.25rem;
border: solid 0.0625 $border-grey;
margin-bottom: 7%;
background-color: $whiteColor;
color: $darkGreyColor;
cursor: pointer;
}
.alert-form {
margin:-1rem;
}
.alert-danger {
color: $redColor;
}
.border-card-color {
border-color: $whiteColor;
}
.alert-form-group {
height: 1rem;
text-align: left;
}
.login-form-group {
text-align: left;
margin-right: 5%;
margin-left: 5%;
}
.login-separator {
display: none;
}
@media all and (orientation: landscape) and (max-width: 766px){
.form-login {
top: 0;
}
}
@media (max-width: 767px) {
.form-login {
top: 0;
height: 100%;
}
.form-BG {
border-radius: 0;
}
.icon-logo-v2 {
width: 8rem;
height: 5rem;
}
.login-separator {
display: block;
width: 40%;
}
.background-allPage {
background-image: none;
}
}

View File

@@ -0,0 +1,26 @@
import React, {Component} from 'react';
import InstallationSchedulingForPackages from './components/installationScheduling/InstallationSchedulingForPackages.jsx';
class InstallationSchedulingContainer extends Component {
render() {
const {orderPackages, areComponentsDisabled, isInstallationSet, isInstallationInPackage, areAllShippingDatesConfirmed} = this.props.params;
return (
<span>
{orderPackages &&
orderPackages.map(orderPackage =>
<InstallationSchedulingForPackages key={orderPackage.idPackage}
orderPackage={orderPackage}
areComponentsDisabled={areComponentsDisabled}
isInstallationSet={isInstallationSet}
isInstallationInPackage={isInstallationInPackage}
areAllShippingDatesConfirmed={areAllShippingDatesConfirmed}
noOfPackages={orderPackages.length}/>)
}
</span>
);
}
}
export default InstallationSchedulingContainer;

View File

@@ -0,0 +1,78 @@
import React, {Component} from 'react';
import {Container, TabContent, TabPane, Nav, NavItem, NavLink, Row, Col} from 'reactstrap';
import classnames from 'classnames';
import './style/Orders.css';
import OrdersDataContainer from './OrdersDataContainer.jsx';
import ProcessContainer from './ProcessContainer.jsx';
import {orderTexts} from '../../constants/ordersConstants';
class OrdersContainer extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
activeTab: '1'
};
}
toggle(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
render() {
const urlParams = this.props.match.params;
return (
<Container fluid={true} id="orders-container">
<Row>
<Col lg="12" md="12" sm="12" xs="12">
{
urlParams.idOrder ?
<ProcessContainer idOrder={urlParams.idOrder}/> :
<div id="orders-list-container">
<Nav tabs>
<NavItem>
<NavLink id="activeOrders" className={classnames({active: this.state.activeTab === '1'}) + ' orders-tab'}
onClick={() => { this.toggle('1'); }}>
{orderTexts.labels.ACTIVE_ORDERS}
</NavLink>
</NavItem>
<NavItem>
<NavLink id="historyOrders" className={classnames({ active: this.state.activeTab === '2' }) + ' orders-tab'}
onClick={() => { this.toggle('2'); }}>
{orderTexts.labels.ORDER_HISTORY}
</NavLink>
</NavItem>
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<Row>
<Col lg="12" md="12" sm="12" xs="12">
<OrdersDataContainer type="active" />
</Col>
</Row>
</TabPane>
<TabPane tabId="2">
<Row>
<Col lg="12" md="12" sm="12" xs="12">
<OrdersDataContainer type="history" />
</Col>
</Row>
</TabPane>
</TabContent>
</div>
}
</Col>
</Row>
</Container>
);
}
}
export default OrdersContainer;

View File

@@ -0,0 +1,57 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Alert, Row, Col} from 'reactstrap';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import {getActiveOrders, getHistoryOrders} from '../../actions/orders/ordersActions';
import OrderList from './components/OrderList.jsx';
import OrderListHeader from './components/OrderListHeader.jsx';
import {orderTexts} from '../../constants/ordersConstants';
class OrdersDataContainer extends Component {
componentDidMount() {
this.props.dispatch(getActiveOrders());
this.props.dispatch(getHistoryOrders());
}
checkIfOrdersExistForUser(orders) {
return orders.every((order) => {
return 'isMyOrder' in order && !order.isMyOrder;
})
}
render() {
const {activeOrders, historyOrders, type, isLoading} = this.props;
const orders = type ? type === 'active' ? activeOrders : historyOrders : {};
const mainTitleOrder = type.charAt(0).toUpperCase() + type.slice(1);
return (
<Row>
<Col lg="12" xs="12">
<WiaasBox id={type + "-orders-container"} mainTitle={mainTitleOrder + " Orders"} customHeader={OrderListHeader} customHeaderParams={type}>
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{ (orders && !isLoading) &&
<OrderList orders={orders} type={type}/>
}
{
((orders && orders.length === 0 && !isLoading) || (orders && this.checkIfOrdersExistForUser(orders))) &&
<Alert color="info">{orderTexts.labels.NO_RECORDS}</Alert>
}
</WiaasBox>
</Col>
</Row>
);
}
}
const mapStateToProps = (state) => ({
activeOrders: state.ordersReducer.activeOrders,
historyOrders: state.ordersReducer.historyOrders,
isLoading: state.ordersReducer.isLoading
});
export default connect(mapStateToProps)(OrdersDataContainer);

View File

@@ -0,0 +1,267 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import {fetchOrderInfo, getAllDataForInstallation} from '../../actions/orders/processActions';
import OrderInfo from './components/OrderInfo.jsx';
import OrderProcess from './components/process/OrderProcess.jsx';
import ProcessPackage from './components/packages/ProcessPackage.jsx';
import OrderComments from './components/OrderComments.jsx';
import PackagesNav from './components/PackagesNav.jsx';
import PriceHelper from '../../helpers/coMarket/PriceHelper';
import ProcessNavContainer from './ProcessNavContainer.jsx';
import OrderDocuments from './components/OrderDocuments.jsx';
import {orderTexts} from '../../constants/ordersConstants';
import './style/ProcessContainer.css';
import './style/ProcessNavContainer.css';
const priceHelper = new PriceHelper();
const usedForDirective = 'installationScheduling';
const stepsNameForInstallation = {
firstStepEnabled: 5,
lastStepEnabled: 6
};
const fileType = 'installationProtocol';
class ProcessContainer extends Component {
constructor(props){
super(props);
this.onViewChange = this.onViewChange.bind(this);
this.getActiveView = this.getActiveView.bind(this);
this.onPackageFilter = this.onPackageFilter.bind(this);
this.filterPackages = this.filterPackages.bind(this);
this.state = {
activeView : 'info',
packageNameFilter: 'all',
isSchedulingDisabled: {},
isOnePackageAndInstallationNotExists: {},
isInstallationInPackage: {},
orderPackagePairs: [],
tooltipOpen: false,
isScheduleBtnClicked: false
};
}
componentDidMount() {
this.props.dispatch(fetchOrderInfo(this.props.idOrder));
this.props.dispatch(getAllDataForInstallation(this.props.idOrder, usedForDirective, stepsNameForInstallation, fileType));
const orderPackagePairs = [];
const isSchedulingDisabled = {};
isSchedulingDisabled[this.props.idOrder] = true;
const newState = Object.assign(isSchedulingDisabled, this.state.isSchedulingDisabled);
if(this.props.orderInfo) {
this.props.orderInfo.packages.forEach(orderPackage => {
orderPackagePairs.push(orderPackage.idOrder + '-' + orderPackage.idPackage);
});
}
this.setState({
orderPackagePairs,
isSchedulingDisabled: newState
});
}
componentWillReceiveProps(nextProps) {
const {installCompanies, isComponentDisabled, earliestInstallDate, areAllShippingDatesConfirmed} = nextProps;
const allPackagesScheduleInstallDisabled = [];
const areComponentsDisabled = {};
const isInstallationSet = {};
const isOnePackageAndInstallationNotExists = {};
const isInstallationInPackage = {};
if(installCompanies && isComponentDisabled && earliestInstallDate) {
if(nextProps.orderInfo) {
nextProps.orderInfo.packages.forEach(orderPackage => {
const idOrder = orderPackage && orderPackage.idOrder;
const idOrderPackagePair = orderPackage ? idOrder + '-' + orderPackage.idPackage : '';
orderPackage.idOrderPackagePair = idOrderPackagePair;
areComponentsDisabled[idOrder] = this.checkIfComponentIsDisabled(orderPackage.idOrder, isComponentDisabled, earliestInstallDate);
const availableCompanies = {};
const selectedCompanies = {};
isInstallationSet[idOrderPackagePair] = false;
if(installCompanies) {
availableCompanies[idOrderPackagePair] = idOrderPackagePair in installCompanies.available ? installCompanies.available[idOrderPackagePair] : [];
selectedCompanies[idOrderPackagePair] = idOrderPackagePair in installCompanies.selected ? installCompanies.selected[idOrderPackagePair] : {};
if (idOrderPackagePair in selectedCompanies && Object.keys(selectedCompanies[idOrderPackagePair]).length > 0) {
isInstallationSet[idOrderPackagePair] = true;
}
if(availableCompanies[idOrderPackagePair].length === 0 && Object.keys(selectedCompanies[idOrderPackagePair]).length === 0) {
isOnePackageAndInstallationNotExists[orderPackage.idOrder] = nextProps.orderInfo.packages.length === 1 ? true : false;
isInstallationInPackage[idOrderPackagePair] = false;
} else {
isInstallationInPackage[idOrderPackagePair] = true;
}
}
const isSchedulingDisabled = areComponentsDisabled[idOrder] || (isInstallationSet && !isInstallationSet[idOrderPackagePair]);
allPackagesScheduleInstallDisabled.push(isSchedulingDisabled);
});
const isSchedulingDisabled = Object.assign({}, this.state.isSchedulingDisabled);
isSchedulingDisabled[nextProps.orderInfo.info.id] = allPackagesScheduleInstallDisabled.every(isDisabled => {return isDisabled === true;});
this.setState({
isSchedulingDisabled,
areComponentsDisabled,
isInstallationSet,
isOnePackageAndInstallationNotExists,
isInstallationInPackage,
areAllShippingDatesConfirmed,
packages: nextProps.orderInfo.packages
});
}
}
}
checkIfComponentIsDisabled(idOrder, isComponentDisabled, earliestInstallDate) {
if(isComponentDisabled && usedForDirective in isComponentDisabled && idOrder in isComponentDisabled[usedForDirective]) {
const isScheduleComponentDisabled = isComponentDisabled[usedForDirective][idOrder];
if(earliestInstallDate) {
return isScheduleComponentDisabled || (!(idOrder in earliestInstallDate) || earliestInstallDate[idOrder] === '-');
}
}
return true;
}
calculatetTotalPrice(packages) {
let fixedPrice = priceHelper.sumPrices(packages.map(pkg => { return pkg.units * pkg.packageFixedPrice}));
let recurrentPrice = priceHelper.sumPrices(packages.map(pkg => { return pkg.units * pkg.packageRecuringPrice}));
let servicesPrice = priceHelper.sumPrices(packages.map(pkg => { return pkg.units * pkg.packageServicePrice}));
return {
fixedPrice,
recurrentPrice: priceHelper.sumPrices([recurrentPrice, servicesPrice]),
periodUnit: packages[0].periodUnit,
currency: packages[0].packageCurrency.currency
}
}
onViewChange(activeView){
if(activeView === 'documents'){
this.props.dispatch(fetchOrderInfo(this.props.idOrder));
}
this.setState({activeView});
}
getActiveView() {
return this.state.activeView;
}
onPackageFilter(packageNameFilter) {
this.setState({packageNameFilter});
}
filterPackages(orderPackage){
if(this.state.packageNameFilter === 'all') {
return true;
}else{
return orderPackage.packageName === this.state.packageNameFilter;
}
}
getProcess(process){
const processKeys = Object.keys(process) || [];
return processKeys.length > 0 ? process[processKeys[0]] : {};
}
getButtonClass() {
if(this.props.orderInfo) {
return this.state.isSchedulingDisabled[this.props.orderInfo.id] ? 'schedule-inactive' : 'schedule-active';
}
return 'schedule-inactive';
}
render() {
const {orderInfo, isLoading} = this.props;
return (
<div id="order-info">
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(orderInfo && orderInfo.info && !isLoading) &&
<div>
<WiaasBox
customHeader={ProcessNavContainer}
customHeaderParams={{
orderInfo: orderInfo.info,
packages: orderInfo.packages,
onViewChange: this.onViewChange,
getActiveView: this.getActiveView,
installationData: this.state
}}>
<OrderInfo totalPrice={this.calculatetTotalPrice(orderInfo.packages)} orderDetails={orderInfo} installationData={this.state}/>
</WiaasBox>
{
this.state.activeView !== 'info' &&
<div className="components-link">
<div className="link-line"></div>
</div>
}
{
this.state.activeView === 'info' &&
<OrderProcess
onViewChange={this.onViewChange}
orderStatus={orderInfo.status}
orderProcess={this.getProcess(orderInfo.process)}/>
}
{
this.state.activeView === 'packages' &&
<WiaasBox id="order-packages"
customHeader={PackagesNav}
customHeaderParams={{packages: orderInfo.packages, onPackageFilter: this.onPackageFilter, packageNameFilter: this.state.packageNameFilter}}
>
{
orderInfo.packages.filter(this.filterPackages).map(orderPackage =>
<ProcessPackage key={orderPackage.idPackage}
onViewChange={this.onViewChange}
idCommercialLead={orderInfo.info.idCommercialLead}
orderPackage={orderPackage}/>
)
}
</WiaasBox>
}
{
this.state.activeView === 'comments' &&
<OrderComments orderInfo={orderInfo.info} orderComments={orderInfo.orderComments} orderPackages={orderInfo.packages}/>
}
{
this.state.activeView === 'documents' &&
<OrderDocuments idOrder={orderInfo.info.id} />
}
</div>
}
{
(orderInfo && !orderInfo.info && !isLoading) &&
<div className="no-rigths">
{orderTexts.labels.NOT_AVAILABLE}!
</div>
}
</div>
);
}
}
const mapStateToProps = (state) => ({
isCompanyAdmin: state.auth.isCompanyAdmin,
orderInfo: state.processReducer.orderInfo,
confirmationDates: state.processReducer.confirmationDates,
isLoading: state.processReducer.isLoading,
isNextStepWanted: state.processReducer.isNextStepWanted,
isComponentDisabled: state.processReducer.isComponentDisabled,
earliestInstallDate: state.processReducer.earliestInstallDate,
installCompanies: state.processReducer.installCompanies,
areAllShippingDatesConfirmed: state.processReducer.areAllShippingDatesConfirmed
});
export default connect(mapStateToProps)(ProcessContainer);

View File

@@ -0,0 +1,110 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Button, Tooltip} from 'reactstrap';
import {orderTexts} from '../../constants/ordersConstants';
import {setDialogContent, setDialogOpenFlag} from '../../actions/dialog/dialogActions';
import InstallationSchedulingContainer from './InstallationSchedulingContainer.jsx';
class ProcessNavContainer extends Component {
constructor(props) {
super(props);
this.state = {
tooltipOpen: false,
isScheduleBtnClicked: false
};
this.toggle = this.toggle.bind(this);
this.handleScheduleBtn = this.handleScheduleBtn.bind(this);
this.setContentDialog = this.setContentDialog.bind(this);
this.openDialogContent = this.openDialogContent.bind(this);
}
getClass(buttonView) {
const view = this.props.params.getActiveView();
return buttonView === view ? 'process-nav-div selected-nav' : 'process-nav-div';
}
toggle() {
this.setState({
tooltipOpen: !this.state.tooltipOpen
});
}
handleScheduleBtn() {
this.setState({isScheduleBtnClicked: !this.state.isScheduleBtnClicked});
}
setContentDialog() {
const {installationData} = this.props.params;
return {
class: 'installation-scheduling',
hasCloseIcon: true,
header: orderTexts.labels.INSTALLATION_SCHEDULE_HEADER,
TagName: InstallationSchedulingContainer,
params: {
orderPackages: installationData.packages,
areComponentsDisabled: installationData.areComponentsDisabled,
isInstallationSet: installationData.isInstallationSet,
isInstallationInPackage: installationData.isInstallationInPackage,
areAllShippingDatesConfirmed: installationData.areAllShippingDatesConfirmed,
openDialogContent: this.openDialogContent
}
};
}
openDialogContent() {
if(!this.props.params.installationData.isSchedulingDisabled[this.props.params.orderInfo.id]) {
const dialogContent = this.setContentDialog();
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
}
getButtonClass() {
const {installationData, orderInfo} = this.props.params;
return installationData.isSchedulingDisabled[orderInfo.id] ? 'schedule-inactive' : 'schedule-active';
}
render() {
const {orderInfo, onViewChange, installationData} = this.props.params;
return (
<Row id="process-nav" className="process-nav">
<Col xl="2" lg="2" md="4" sm="12" xs="12" className="process-nav-buttons">
<div className="wiaas-main-title">
{orderTexts.labels.ORDER} {orderInfo.orderNumber} <div className={'status-layer ' + orderInfo.status}>{orderTexts.statuses[orderInfo.status]}</div>
</div>
</Col>
<Col xl="2" lg="2" md="2" sm="12" xs="12" className="process-nav-buttons">
<Button className={"schedule-installation-btn " + this.getButtonClass()}
id="schedule-installation-button"
onClick={this.openDialogContent}>
{orderTexts.buttons.SCHEDULE_INSTALLATION}
</Button>
<Tooltip placement="bottom" isOpen={this.state.tooltipOpen} target="schedule-installation-button" toggle={this.toggle}>
{
installationData.isSchedulingDisabled[orderInfo.id]
? installationData.isOnePackageAndInstallationNotExists[orderInfo.id]
? orderTexts.labels.SCHEDULE_INSTALLATION_NOT_EXIST
: orderTexts.labels.SCHEDULE_INSTALLATION_DISABLED
: orderTexts.labels.SCHEDULE_INSTALLATION_ENABLED
}
</Tooltip>
</Col>
<Col xl="4" lg="6" md="6" sm="12" xs="12" className="process-menu">
<div className={this.getClass('info')} onClick={()=>{onViewChange('info')}}>{orderTexts.labels.ORDER_INFO}</div>
<div className={this.getClass('packages')} onClick={()=>{onViewChange('packages')}}>{orderTexts.labels.PACKAGES}</div>
<div className={this.getClass('comments')} onClick={()=>{onViewChange('comments')}}>{orderTexts.labels.COMMENTS}</div>
<div className={this.getClass('documents')} onClick={()=>{onViewChange('documents')}}>{orderTexts.labels.DOCUMENTS}</div>
</Col>
</Row>
);
}
}
export default connect()(ProcessNavContainer);

View File

@@ -0,0 +1,79 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Button} from 'reactstrap';
import {Link} from 'react-router-dom';
import {WiaasTableRow, WiaasTableCol} from '../../../mainComponents/table/WiaasTable.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
import '../style/Orders.css';
import OrderPackage from './OrderPackage.jsx';
class ActiveOrderItem extends Component {
constructor(props) {
super(props);
this.state = {
showPackages: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
showPackages: !this.state.showPackages
});
}
getIconClass(type) {
return 'fa fa-angle-' + type + ' toggle-view-packages';
}
render() {
const {order, isViewAllOrdersChecked} = this.props;
return (
<div>
<WiaasTableRow className={"order-central-row order-border-" + order.status}>
<WiaasTableCol header="#">
{!this.state.showPackages && <i className={this.getIconClass('down')} aria-hidden="true" onClick={this.toggle}></i>}
{this.state.showPackages && <i className={this.getIconClass('up')} aria-hidden="true" onClick={this.toggle}></i>}
<i className="fa fa-list-alt package-photo" aria-hidden="true" />
<div className="order-number">{order.orderNumber}</div>
</WiaasTableCol>
<WiaasTableCol header={orderTexts.labels.REFERENCE}>{order.reference ? order.reference : '-'}</WiaasTableCol>
{isViewAllOrdersChecked['active'] && <WiaasTableCol header={orderTexts.labels.PLACED_BY}>{order.customerName ? order.customerName : ''}</WiaasTableCol>}
<WiaasTableCol header={orderTexts.labels.ORDER_DATE}>{order.orderDate ? order.orderDate : '-'}</WiaasTableCol>
<WiaasTableCol header={orderTexts.labels.EST_DELIVERY}>{order.estimatedDeliveryDate ? order.estimatedDeliveryDate : '-'}</WiaasTableCol>
<WiaasTableCol header="Catalogue">
<div className="order-item-cl">
<img className="cl-photo" src="static/img/man-icon.png" alt="Catalogue" />
<div className="cl-details">
<div className="cl-contact-name">{order.clContactName}</div>
<div className="cl-name">{order.clName}</div>
</div>
</div>
</WiaasTableCol>
<WiaasTableCol header={orderTexts.labels.AMOUNT}>
{order.orderCurrency && order.orderCurrency.currency
? order.orderTotalPrice.toLocaleString() + ' ' + order.orderCurrency.currency
: parseFloat(order.orderTotalPrice).toLocaleString()}
</WiaasTableCol>
<WiaasTableCol header={orderTexts.labels.STATUS}>
<div className={'status-icon ' + order.status}></div>{order.status}
</WiaasTableCol>
<WiaasTableCol header="Buttons details">
<Link to={'/orders/' + order.id} className="actions-link">
<Button color="secondary" className="actions-button">{orderTexts.buttons.DELIVERY_DETAILS}</Button>
</Link>
</WiaasTableCol>
</WiaasTableRow>
{ this.state.showPackages && <OrderPackage order={order} type="active"/> }
</div>
);
}
}
const mapStateToProps = (state) => ({
isViewAllOrdersChecked: state.ordersReducer.isViewAllOrdersChecked
});
export default connect(mapStateToProps)(ActiveOrderItem);

View File

@@ -0,0 +1,26 @@
import React, {Component} from 'react';
import { Editor } from '@tinymce/tinymce-react';
class AddComment extends Component {
handleEditorChange = (e) => {
const comment = e.target.getContent();
this.props.onEditorChange(comment);
}
render() {
return (
<div>
<Editor
init={{
plugins: 'link textcolor lists colorpicker code',
toolbar: 'undo redo | styleselect | bold italic underline forecolor fontsizeselect | link | alignleft aligncenter alignright | numlist bullist | outdent indent | code'
}}
onChange={this.handleEditorChange}
/>
</div>
);
}
}
export default AddComment;

Some files were not shown because too many files have changed in this diff Show More