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

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;

View File

@@ -0,0 +1,81 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';
import {Link} from 'react-router-dom';
import SupportMail from './SupportMail.jsx';
import {setDialogContent, setDialogOpenFlag} from '../../../actions/dialog/dialogActions';
import {sendSupportMail} from '../../../actions/orders/processActions';
import {orderTexts} from '../../../constants/ordersConstants';
class HistoryOrdersButtons extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.sendSupportMail = this.sendSupportMail.bind(this);
this.openDialogContent = this.openDialogContent.bind(this);
this.state = {
dropdownOpen: false
};
}
toggle() {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
openDialogContent() {
const dialogContent = {
buttons: [
{
color: 'secondary',
name: orderTexts.buttons.SEND,
id: 'send-mail-to-support-btn',
action: this.sendSupportMail
},
{
color: 'secondary',
name: orderTexts.buttons.CANCEL,
id: 'cancel-send-mail-to-support-btn'
}
],
header: orderTexts.labels.SUPPORT_MESSAGE_HEADER,
TagName: SupportMail,
params: {orderInfo: this.props.order, orderPackages: this.props.order.packages}
};
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
sendSupportMail() {
this.props.dispatch(sendSupportMail(this.props.order, this.props.order.packages, this.props.supportText));
}
render() {
const {order} = this.props;
return (
<Dropdown size="sm" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle className="actions-button" caret>
{orderTexts.buttons.ACTIONS}
</DropdownToggle>
<DropdownMenu right>
<DropdownItem className="actions-item" onClick={this.openDialogContent}> {orderTexts.buttons.SUPPORT}
</DropdownItem>
<Link to={'/orders/' + order.id} className="actions-link">
<DropdownItem className="actions-item">
{orderTexts.buttons.DETAILS}
</DropdownItem>
</Link>
</DropdownMenu>
</Dropdown>
)
}
}
const mapStateToProps = (state) => ({
supportText: state.processReducer.supportText
});
export default connect(mapStateToProps)(HistoryOrdersButtons);

View File

@@ -0,0 +1,77 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {WiaasTableRow, WiaasTableCol} from '../../../mainComponents/table/WiaasTable.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
import '../style/Orders.css';
import OrderPackage from './OrderPackage.jsx';
import HistoryOrderButtons from './HistoryOrderButtons.jsx';
class HistoryOrdersItem 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['history'] && <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.DELIVERY_DATE}>{order.deliveryDate}</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>{orderTexts.statuses[order.status]}
</WiaasTableCol>
<WiaasTableCol header="End of life">{order.endOfLife}</WiaasTableCol>
<WiaasTableCol header="Buttons details">
<HistoryOrderButtons order={order}/>
</WiaasTableCol>
</WiaasTableRow>
{ this.state.showPackages && <OrderPackage order={order} type="history"/> }
</div>
);
}
}
const mapStateToProps = (state) => ({
isViewAllOrdersChecked: state.ordersReducer.isViewAllOrdersChecked
});
export default connect(mapStateToProps)(HistoryOrdersItem);

View File

@@ -0,0 +1,72 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Button, Container, Row, Col} from 'reactstrap';
import WiaasBox from '../../../mainComponents/box/WiaasBox.jsx';
import AddComment from './AddComment.jsx';
import {addComment} from '../../../actions/orders/processActions';
import {orderTexts} from '../../../constants/ordersConstants';
class OrderComments extends Component {
constructor(props) {
super(props);
this.onEditorChange = this.onEditorChange.bind(this);
this.addNewComment = this.addNewComment.bind(this);
this.state = {
newComment: ''
};
}
addNewComment(){
this.props.dispatch(addComment(this.props.orderInfo.id, this.state.newComment));
}
onEditorChange(newComment){
this.setState({newComment});
}
getOffset(isOwner){
return isOwner ? 6 : 0;
}
getClassByOwner(isOwner){
return isOwner ? 'mine' : '';
}
render() {
const {orderComments} = this.props;
return (
<div id="order-comments" className="order-comments">
<WiaasBox mainTitle="Comments">
<Container fluid={true} className="order-coments-body">
<Row>
<Col xl="6">
{
orderComments &&
orderComments.map((orderComment, index) =>
<Row key={'order-comment-' + index}>
<Col xl={{size:6, offset:this.getOffset(orderComment.isOwner)}}>
<div className={'order-comment ' + this.getClassByOwner(orderComment.isOwner)} key={'order-comment-' + index}>
<div className="order-comment-header">{orderComment.username} - {orderComment.addDate}</div>
<div dangerouslySetInnerHTML={{__html: orderComment.comment}}></div>
</div>
</Col>
</Row>
)
}
</Col>
<Col xl="6">
<AddComment onEditorChange={this.onEditorChange}/>
<Button onClick={()=>{this.addNewComment()}} className="new-comment-button" color="secondary">{orderTexts.buttons.ADD_COMMENT}</Button>
</Col>
</Row>
</Container>
</WiaasBox>
</div>
);
}
}
export default connect()(OrderComments);

View File

@@ -0,0 +1,26 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import OrderDocumentsGroup from './OrderDocumentsGroup.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
class OrderDocuments extends Component {
render() {
const {orderInfo} = this.props;
return (
<div id="order-documents" className="order-documents">
{
orderInfo.packages.map(orderPackage => <OrderDocumentsGroup key={'order-package-' + orderPackage.idPackage} documentsGroup={orderPackage} />)
}
{
orderInfo.orderDocuments && <OrderDocumentsGroup key={'order-package-0'} documentsGroup={{documents: orderInfo.orderDocuments, packageName: orderTexts.labels.OTHER_DOCS}} />
}
</div>
);
}
}
const mapStateToProps = (state) => ({
orderInfo: state.processReducer.orderInfo
});
export default connect(mapStateToProps)(OrderDocuments);

View File

@@ -0,0 +1,85 @@
import React, {Component} from 'react';
import {Tooltip} from 'reactstrap';
import WiaasBox from '../../../mainComponents/box/WiaasBox.jsx';
import {API_SERVER} from '../../../config';
import FileDownloader from '../../../helpers/FileDownloader';
const fileHandler = new FileDownloader();
const iconTypes = {
doc: 'file-word-o',
docx: 'file-word-o',
odt: 'file-word-o',
ods: 'file-excel-o',
pdf: 'file-pdf-o',
png: 'file-image-o',
jpg: 'file-image-o',
xls: 'file-excel-o',
xlsx: 'file-excel-o',
ppt: 'file-powerpoint-o',
pptx: 'file-powerpoint-o'
}
class OrderDocumentsGroup extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {};
}
toggle(idDocument) {
const obj = {}
obj[idDocument] = !this.state[idDocument];
this.setState(obj);
}
getDocumentIcon(documentType) {
return iconTypes[documentType] || 'file';
}
getDocumentText(document) {
const name = document.documentName + '.' + document.extension;
return name.length > 9 ? name.substring(0, 10) + '...' : name;
}
downloadDocument(document){
const fileUrl = `${API_SERVER}/utils/api/downloadFile?idDocument=${document.idDocument}&fileName=${document.documentName}.${document.extension}&fileType=${document.documentTypeName}`
const fileName = document.documentName + '.' + document.extension;
fileHandler.download(fileUrl, fileName);
}
render() {
const {documentsGroup} = this.props;
return (
<div>
{
documentsGroup.documents.length > 0 &&
<WiaasBox mainTitle={documentsGroup.packageName}>
{
documentsGroup.documents.map(document => <a id={'document-' + document.idDocument} key={'order-document-' + document.idDocument}>
<div onClick={() => {this.downloadDocument(document)}} className="document-link-big">
<i className={'fa fa-4x fa-' + this.getDocumentIcon(document.extension)} aria-hidden="true"></i>
<div>
{this.getDocumentText(document)}
<Tooltip placement="bottom"
delay={{ show: 0, hide: 0}}
container="order-documents"
isOpen={this.state[document.idDocument]}
target={'document-' + document.idDocument}
toggle={()=>this.toggle(document.idDocument)}>
{document.documentName} ({document.extension})
</Tooltip>
</div>
</div></a>)
}
</WiaasBox>
}
</div>)
}
}
export default OrderDocumentsGroup;

View File

@@ -0,0 +1,152 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import moment from 'moment';
import {Container, Row, Col} from 'reactstrap';
import TermsContainer from '../../terms/TermsContainer.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
class OrderInfo extends Component {
constructor(props){
super(props);
this.state = {
acceptedDate: {},
proposedDate: {},
isPreliminaryInstallationDate: true
};
}
componentDidMount() {
this.setInstallationData(this.props);
}
componentWillReceiveProps(nextProps) {
this.setInstallationData(nextProps);
}
setInstallationData(props) {
const acceptedDate = {};
const proposedDate = {};
const idOrder = props.orderDetails.info.id;
const {isInstallationInPackage} = props.installationData;
const {confirmationDates, areAllShippingDatesConfirmed} = props;
const isPreliminaryInstallationDate = areAllShippingDatesConfirmed && idOrder in areAllShippingDatesConfirmed ? !areAllShippingDatesConfirmed[idOrder] : true;
const isInstallationInOrder = Object.keys(isInstallationInPackage).some(idOrderPackage => {return isInstallationInPackage[idOrderPackage] === true;});
props.orderDetails.packages.forEach(orderPackage => {
const idOrderPackagePair = idOrder + '-' + orderPackage.idPackage;
if(confirmationDates && confirmationDates[idOrderPackagePair]) {
const packageDates = confirmationDates[idOrderPackagePair];
const accepted = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'accepted'});
acceptedDate[idOrderPackagePair] = accepted ? moment(accepted).format('Do MMM, YYYY') : '';
const proposed = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'proposed'});
proposedDate[idOrderPackagePair] = proposed ? moment(proposed).format('Do MMM, YYYY') : '';
}
});
this.setState({acceptedDate, proposedDate, isPreliminaryInstallationDate, isInstallationInOrder});
}
render() {
const {totalPrice, installationData} = this.props;
const {acceptedDate, proposedDate, isPreliminaryInstallationDate, isInstallationInOrder} = this.state;
const orderPackages = installationData.packages;
const isInstallationInPackage = installationData.isInstallationInPackage;
const orderInfo = this.props.orderInfo.info;
return (
<Container fluid={true} id="order-info-description">
<Row>
<Col xl="2">
<div className="subtitle"><h6>{orderTexts.labels.ORDER_DATE}:</h6></div>
<span>{orderInfo.orderDate}</span>
<div className="subtitle"><h6>{orderTexts.labels.SOLD_BY}:</h6></div>
<span>{orderInfo.commercialLead}</span>
</Col>
<Col xl="2">
<div>
<div className="subtitle"><h6>{orderTexts.labels.REFERENCE}:</h6></div>
<span>{orderInfo.reference || '-'}</span>
</div>
<div>
<div className="subtitle"><h6>{orderTexts.labels.BID}:</h6></div>
<span>{orderInfo.tender || '-'}</span>
</div>
</Col>
{ isInstallationInOrder &&
<Col xl="3">
<div>
<div className="subtitle">
{ isPreliminaryInstallationDate
? <h6>{orderTexts.labels.PRELIMINARY_INSTALLATION_DATE_LABEL}:</h6>
: <h6>{orderTexts.labels.INSTALLATION_DATE}:</h6>
}
</div>
{ orderPackages &&
orderPackages.map(orderPackage =>
<div key={'package-install-date-' + orderPackage.idOrderPackagePair}>
{ isInstallationInPackage[orderPackage.idOrderPackagePair] &&
<div>
{ orderPackages.length > 1 &&
<span>{orderPackage.packageName}: </span>
}
<span className="installation-date-per-package">
{ acceptedDate && acceptedDate[orderPackage.idOrderPackagePair]
? acceptedDate[orderPackage.idOrderPackagePair]
: proposedDate && proposedDate[orderPackage.idOrderPackagePair]
? proposedDate[orderPackage.idOrderPackagePair]
: orderTexts.labels.NOT_SET
}
</span>
</div>
}
</div>
)
}
</div>
</Col>
}
<Col xl="2">
<div className="subtitle"><h6>{orderTexts.labels.TOTAL_DELVIERY_PRICE}:</h6></div>
<span>{totalPrice.fixedPrice.toLocaleString()} {totalPrice.currency}</span>
{
totalPrice.recurrentPrice > 0 &&
<div>
<div className="subtitle"><h6>{orderTexts.labels.TOTAL_RECURRENT_PRICE}:</h6></div>
<span>
{totalPrice.recurrentPrice.toLocaleString() + ' ' + totalPrice.currency}
</span>
</div>
}
</Col>
<Col xl="2">
<div>
<div className="subtitle"><h6>{orderTexts.labels.PROJECT}:</h6></div>
<span>{orderInfo.projectName || '-'}</span>
</div>
<div className="terms-link">
<i className="fa fa-link"></i>
<span className="terms-label">
<TermsContainer idTerms={orderInfo.idTerms}/>
</span>
</div>
</Col>
</Row>
</Container>
);
}
}
const mapStateToProps = (state) => ({
orderInfo: state.processReducer.orderInfo,
confirmationDates: state.processReducer.confirmationDates,
areAllShippingDatesConfirmed: state.processReducer.areAllShippingDatesConfirmed
});
export default connect(mapStateToProps)(OrderInfo);

View File

@@ -0,0 +1,75 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import ActiveOrderItem from './ActiveOrderItem.jsx';
import HistoryOrderItem from './HistoryOrderItem.jsx';
import {WiaasTable, WiaasTableHeader, WiaasTableBody} from '../../../mainComponents/table/WiaasTable.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
class OrderList extends Component {
constructor(props) {
super(props);
this.getHeadersByType = this.getHeadersByType.bind(this);
}
getHeadersByType(type) {
const activeOrdersHeader = [
orderTexts.headers.ORDER,
orderTexts.headers.REFERENCE,
orderTexts.headers.ORDER_DATE,
orderTexts.headers.ESTIMATED_DATE,
orderTexts.headers.COMMERCIAL_LEAD,
orderTexts.headers.AMOUNT,
orderTexts.headers.STATUS,
''
];
const historyOrdersHeader = [
orderTexts.headers.ORDER,
orderTexts.headers.REFERENCE,
orderTexts.headers.ORDER_DATE,
orderTexts.headers.DELIVERY_DATE,
orderTexts.headers.COMMERCIAL_LEAD,
orderTexts.headers.AMOUNT,
orderTexts.headers.STATUS,
orderTexts.headers.END_OF_LIFE,
''
];
if(this.props.isCompanyAdmin && this.props.isViewAllOrdersChecked[type]) {
activeOrdersHeader.splice(2, 0, orderTexts.headers.PLACED_BY);
activeOrdersHeader.join();
historyOrdersHeader.splice(2, 0, orderTexts.headers.PLACED_BY);
historyOrdersHeader.join();
}
return type === 'active' ? activeOrdersHeader : historyOrdersHeader;
}
render() {
const {orders, type, isCompanyAdmin, isViewAllOrdersChecked} = this.props;
const TagName = type && type === 'active' ? ActiveOrderItem : HistoryOrderItem;
return (
<div>
<WiaasTable>
<WiaasTableHeader headers={this.getHeadersByType(type)}/>
<WiaasTableBody>
{
orders &&
orders.map((order, index) =>
(('isMyOrder' in order && order.isMyOrder) || (isCompanyAdmin && isViewAllOrdersChecked[type])) &&
<TagName key={order.orderNumber} order={order} />
)
}
</WiaasTableBody>
</WiaasTable>
</div>
);
}
}
const mapStateToProps = (state) => ({
isCompanyAdmin: state.auth.isCompanyAdmin,
isViewAllOrdersChecked: state.ordersReducer.isViewAllOrdersChecked
});
export default connect(mapStateToProps)(OrderList);

View File

@@ -0,0 +1,56 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Input} from 'reactstrap';
import '../style/OrdersList.css';
import {setViewAllOrdersFlag} from '../../../actions/orders/ordersActions';
import {orderTexts} from '../../../constants/ordersConstants';
class OrderListHeader extends Component {
constructor(props) {
super(props);
this.state = {
isViewAllOrdersChecked: false
};
this.handleInputChange = this.handleInputChange.bind(this);
}
componentDidMount() {
const isViewAllChecked = {};
isViewAllChecked[this.props.params] = this.state.isViewAllOrdersChecked;
this.props.dispatch(setViewAllOrdersFlag(isViewAllChecked));
}
handleInputChange(e) {
this.setState({
isViewAllOrdersChecked: !this.state.isViewAllOrdersChecked
});
const isViewAllChecked = {};
isViewAllChecked[this.props.params] = e.target.checked;
this.props.dispatch(setViewAllOrdersFlag(isViewAllChecked));
}
render() {
const {isCompanyAdmin} = this.props;
return (
<Row id="view-all-orders-checkbox">
{isCompanyAdmin &&
<Col xl="10">
<Input type="checkbox"
name="isViewAllOrdersChecked"
checked={this.state.isViewAllOrdersChecked}
onChange={this.handleInputChange}
className="view-all-orders-checkbox" />
<span className="view-all-orders-text">{orderTexts.labels.VIEW_ALL_ORDERS}</span>
</Col>
}
</Row>
);
}
}
const mapStateToProps = (state) => ({
isCompanyAdmin: state.auth.isCompanyAdmin
});
export default connect(mapStateToProps)(OrderListHeader);

View File

@@ -0,0 +1,92 @@
import React, {Component} from 'react';
import {Col} from 'reactstrap';
import WiaasBox from '../../../mainComponents/box/WiaasBox.jsx';
import {WiaasTable, WiaasTableHeader, WiaasTableBody, WiaasTableRow, WiaasTableCol} from '../../../mainComponents/table/WiaasTable.jsx';
import {orderTexts} from '../../../constants/ordersConstants';
import '../style/Orders.css';
class OrderPackage extends Component {
calculateRecuringPrice(packageDetails) {
return packageDetails.units * (parseFloat(packageDetails.packageRecuringPrice) + parseFloat(packageDetails.packageServicePrice));
}
calculateQuantityPrice(quantity, price, recurringPrice = 0) {
return quantity * parseFloat(price + recurringPrice);
}
getHeadersByType(type) {
if(type === 'active') {
return ['Package', 'Price', 'Services and support', 'Delivery active step'];
}
return ['Package', 'Price', 'Services and support', 'End of Life'];
}
render() {
const {order, type} = this.props;
return (
<WiaasTableRow className={"order-border-" + order.status}>
<Col lg="3" sm="12" xs="12">
<WiaasBox mainTitle="Order Details">
<div className="order-details-container">
<WiaasTableRow id={'delivery-address-' + order.id}>
<Col lg="4" sm="4" xs="4">{orderTexts.labels.DELIVERY_ADDRESS}:</Col>
<Col lg="8" sm="8" xs="8">{order.deliveryAddress}</Col>
</WiaasTableRow>
<WiaasTableRow id={'phone-' + order.id}>
<Col lg="4" sm="4" xs="4">{orderTexts.labels.PHONE_NUMBER}:</Col>
<Col lg="8" sm="8" xs="8">{order.phone}</Col>
</WiaasTableRow>
<WiaasTableRow id={'mail-' + order.id}>
<Col lg="4" sm="4" xs="4">{orderTexts.labels.MAIL}:</Col>
<Col lg="8" sm="8" xs="8">{order.mail}</Col>
</WiaasTableRow>
<WiaasTableRow id={'commercial-lead-phone-' + order.id}>
<Col lg="4" sm="4" xs="4">{order.clName} {orderTexts.labels.PHONE_NUMBER}:</Col>
<Col lg="8" sm="8" xs="8">{order.commercialLeadPhone}</Col>
</WiaasTableRow>
<WiaasTableRow id={'mail-' + order.id}>
<Col lg="4" sm="4" xs="4">{order.clName} {orderTexts.labels.MAIL}:</Col>
<Col lg="8" sm="8" xs="8">{order.commercialLeadMail}</Col>
</WiaasTableRow>
</div>
</WiaasBox>
</Col>
<Col lg="9" sm="12" xs="12">
<WiaasBox mainTitle="Items">
<WiaasTable>
<WiaasTableHeader headers={this.getHeadersByType(type)}/>
<WiaasTableBody>
{order.packages.map((orderPackage, index) =>
<WiaasTableRow className="order-central-row" key={orderPackage.idOrder + '-' + orderPackage.idPackage}>
<WiaasTableCol header="Package" className="package-info-col">
<div className="package-name">{orderPackage.units} x {orderPackage.packageName}</div>
</WiaasTableCol>
<WiaasTableCol header="Price">
{this.calculateQuantityPrice(orderPackage.units, orderPackage.packageFixedPrice).toLocaleString()} {orderPackage.packageCurrency && orderPackage.packageCurrency.currency} {' '}
({orderPackage.paymentType})
</WiaasTableCol>
<WiaasTableCol header="Services and support">
{this.calculateQuantityPrice(orderPackage.units, orderPackage.packageServicePrice, orderPackage.packageRecuringPrice).toLocaleString() + ' / ' + orderPackage.periodUnit + ' '}
{orderTexts.labels.EXTEND} {orderPackage.periodUnit} (Max {orderPackage.maxContractPeriod})
</WiaasTableCol>
<WiaasTableCol>
{
type === 'active'
? orderPackage.shortDesc ? orderPackage.shortDesc : '-'
: orderPackage.endOfLife ? orderPackage.endOfLife : '-'
}
</WiaasTableCol>
</WiaasTableRow>
)}
</WiaasTableBody>
</WiaasTable>
</WiaasBox>
</Col>
</WiaasTableRow>
);
}
}
export default OrderPackage;

View File

@@ -0,0 +1,35 @@
import React, {Component} from 'react';
import {Row, Col} from 'reactstrap';
import {orderTexts} from '../../../constants/ordersConstants';
class PackageNav extends Component {
getClass(packageName) {
return this.props.params.packageNameFilter === packageName ? 'process-nav-div selected-nav' : 'process-nav-div';
}
render() {
const {packages, onPackageFilter} = this.props.params;
return (
<Row id="process-nav" className="process-nav">
<Col xl="1" lg="2" md="3" sm="12" xs="12" className="process-nav-buttons">
<div className="wiaas-main-title">
{orderTexts.labels.PACKAGES}
</div>
</Col>
<Col xl="9" lg="9" md="4" xs="12" className="process-menu">
<div className={this.getClass('all')} onClick={()=>{onPackageFilter('all')}}>{orderTexts.buttons.ALL}</div>
{
packages.length > 0 &&
packages.map((orderPackage) => <div key={'menu-package-' + orderPackage.idPackage}
onClick={()=>{onPackageFilter(orderPackage.packageName)}}
className={this.getClass(orderPackage.packageName)}>{orderPackage.packageName}</div>)
}
</Col>
</Row>
);
}
}
export default PackageNav;

View File

@@ -0,0 +1,121 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, FormGroup, Label, Input} from 'reactstrap';
import {setSupportMessage} from '../../../actions/orders/processActions';
import {orderTexts} from '../../../constants/ordersConstants';
class SupportMail extends Component {
constructor(props) {
super(props);
this.state = {
supportText: ''
};
this.setSupportMessage = this.setSupportMessage.bind(this);
}
calculateFixedPrice(quantity, price) {
return quantity*price;
}
calculateRecurrentPrice(orderPackage) {
return orderPackage.units*(orderPackage.packageRecuringPrice + orderPackage.packageServicePrice);
}
handleInputChange(event) {
this.setState({supportText: event.target.value});
event.preventDefault();
}
setSupportMessage() {
this.props.dispatch(setSupportMessage(this.state.supportText));
}
render() {
const {orderInfo, orderPackages} = this.props.params;
return (
<span id="send-support-mail-container">
{orderInfo && orderPackages &&
<div>
<div id="order-details">
<Row>
<Col>
<h5>{orderTexts.labels.ORDER_DETAILS}</h5>
</Col>
</Row>
<Row>
<Col xl="4">
<span className="subtitle">{orderTexts.labels.ORDER_NUMBER}:</span>
</Col>
<Col xl="8">
<span>{orderInfo.orderNumber}</span>
</Col>
</Row>
<Row>
<Col xl="4">
<span className="subtitle">{orderTexts.labels.LOCATION_DETAILS}:</span>
</Col>
<Col xl="8">
<span>{orderInfo.reference || '-'}</span>
</Col>
</Row>
</div>
<div id="order-items" className="support-details">
<Row>
<Col>
<h5>{orderTexts.labels.ORDER_ITEMS}</h5>
</Col>
</Row>
{ orderPackages.map((orderPackage, mapKey) =>
<div key={'order-package-'+mapKey} className="order-package-details">
<Row>
<Col>
<span className="fa fa-shopping-cart subtitle"></span>
<span className="package-name"> {orderPackage.units} x {orderPackage.packageName}</span>
</Col>
</Row>
{orderPackage.endOfLife &&
<Row>
<Col>
<span className="subtitle">{orderTexts.labels.END_OF_LIFE}:</span>
</Col>
<Col xl="8">
<span>{orderPackage.endOfLife}</span>
</Col>
</Row>
}
<Row>
<Col>
<span className="subtitle">{orderTexts.labels.STATUS}:</span>
</Col>
<Col xl="8">
<span>{orderPackage.status}</span>
</Col>
</Row>
</div>
)}
</div>
<div id="order-support-text" className="support-details">
<Row>
<Col>
<FormGroup>
<Label for="supportText">{orderTexts.labels.ENTER_TEXT}</Label>
<Input id="my-support-mail-text"
type="textarea"
name="supportText"
value={this.state.supportText}
onChange={(e) => this.handleInputChange(e)}
onBlur={this.setSupportMessage}/>
</FormGroup>
</Col>
</Row>
</div>
</div>
}
</span>
);
}
}
export default connect()(SupportMail);

View File

@@ -0,0 +1,165 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import 'react-datepicker/dist/react-datepicker.css';
import moment from 'moment';
import {Row, Col, Button, Popover, PopoverBody} from 'reactstrap';
import '../../style/InstallationScheduling.css';
import {updateInstallationDate, removeMyDate, setSchedulingFlag} from '../../../../actions/orders/processActions';
import {setDialogContent, setDialogOpenFlag} from '../../../../actions/dialog/dialogActions';
import {orderTexts} from '../../../../constants/ordersConstants';
class InstallationSchedulingDatesPerPackage extends Component {
constructor(props) {
super(props);
this.state = {
startDate: undefined,
btnType: '',
nestedModal: false,
closeAll: false,
popoverOpen: false
};
this.toggle = this.toggle.bind(this);
this.openInstallationDialog = this.openInstallationDialog.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleBtnClick = this.handleBtnClick.bind(this);
this.handleBtnAction = this.handleBtnAction.bind(this);
}
componentDidMount() {
this.props.dispatch(setSchedulingFlag(this.props.installationParams.isSchedulingDisabled));
}
handleChange(date) {
this.setState({startDate: date});
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
canUserAcceptOrDecline(dateInfo, lastStatus) {
let additionalCondition = false;
if (lastStatus === 'declined') {
additionalCondition = dateInfo.lastStatus === 'canceled' && dateInfo.isProposedByMe;
}
return !this.props.isComponentDisabled &&
((dateInfo.lastStatus === 'proposed' && !dateInfo.isProposedByMe) ||
(dateInfo.lastStatus === lastStatus && dateInfo.isProposedByMe) ||
additionalCondition);
}
isDateProposedByMe() {
return this.props.dateInfo.lastStatus === 'proposed' && this.props.dateInfo.isProposedByMe === true;
}
isRemoveBtnVisible(confirmationDate) {
return !this.props.isComponentDisabled && this.isDateProposedByMe();
}
handleBtnClick(type) {
this.setState({type});
const upperCaseType = type.charAt(0).toUpperCase() + type.slice(1);
const dialogInstallationContent = {
buttons: [
{
color: 'success',
action: this.handleBtnAction,
name: orderTexts.buttons.YES,
id: type + '-installation-date-confirmation'
}, {
color: 'secondary',
name: orderTexts.buttons.NO,
action: this.openInstallationDialog,
id: 'cancel-' + type + '-installation-date'
}
],
header: upperCaseType + ' installation date confirmation',
body: 'Are you sure you want to ' + type + ' the installation date proposed?'
};
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogInstallationContent));
}
openInstallationDialog() {
this.props.openDialogInstallContent(this.props.installationParams);
}
handleBtnAction() {
let handleFunction = this.state.type === 'remove' ? removeMyDate : updateInstallationDate;
this.props.dispatch(handleFunction(this.props.orderPackage.idOrder, this.props.orderPackage.idPackage, this.props.confirmationDate, this.state.type));
this.openInstallationDialog();
}
getStatusIcon(status) {
const statusesIcons = {
proposed: 'clock-o',
accepted: 'check',
canceled: 'remove',
declined: 'ban',
invalid: 'remove'
};
return 'status-icon-installation-dates fa fa-' + statusesIcons[status];
}
render() {
const {confirmationDate, dateInfo, orderPackage} = this.props;
const idOrderPackageDatePair = orderPackage.idOrder + '-' + orderPackage.idPackage + '-' + confirmationDate;
return (
<div id={"installation-scheduling-dates-container-" + idOrderPackageDatePair} className="installation-scheduling-dates-container">
<span className="installation-date">
<span className={this.getStatusIcon(dateInfo.lastStatus) + ' confirmation-' + dateInfo.lastStatus}></span>
<span className="installation-date-proposed">{moment(confirmationDate).format('Do MMM, YYYY')}</span>
<i className="dates-info-btn fa fa-info-circle"
id={'dates-info-' + idOrderPackageDatePair}
onClick={this.toggle}></i>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'dates-info-' + idOrderPackageDatePair}
container={"installation-scheduling-dates-container-" + idOrderPackageDatePair}
className="dates-info-popover"
toggle={this.toggle}>
<PopoverBody>
{ dateInfo && dateInfo.details &&
dateInfo.details.map((details) =>
<Row key={'confirmation-date-details-' + details.currentDate}>
<Col>
{moment(details.currentDate).format('Do MMM, YYYY LT')}
</Col>
<Col className={"confirmation-" + details.status}>
<span className={this.getStatusIcon(details.status)}></span>
{details.status}
</Col>
<Col>
{details.username} ({details.userType})
</Col>
</Row>
)}
</PopoverBody>
</Popover>
</span>
<span className="confirmation-dates-buttons">
{ this.canUserAcceptOrDecline(dateInfo, 'declined') &&
<Button id={"accept-date-"+idOrderPackageDatePair} className="installation-button install-btn-accept" onClick={() => {this.handleBtnClick('accept')}} >{orderTexts.buttons.ACCEPT}</Button>
}
{ this.canUserAcceptOrDecline(dateInfo, 'accepted') &&
<Button id={"decline-date-"+idOrderPackageDatePair} className="installation-button install-btn-decline" onClick={() => {this.handleBtnClick('decline')}} >{orderTexts.buttons.DECLINE}</Button>
}
{ this.isRemoveBtnVisible(confirmationDate) &&
<Button id={"cancel-remove-installation-dates-"+idOrderPackageDatePair} className="installation-button install-btn-remove" onClick={() => {this.handleBtnClick('remove')}} >{orderTexts.buttons.REMOVE}</Button>
}
</span>
</div>
);
}
}
const mapStateToProps = (state) => ({
confirmationDates: state.processReducer.confirmationDates
});
export default connect(mapStateToProps)(InstallationSchedulingDatesPerPackage);

View File

@@ -0,0 +1,175 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import moment from 'moment';
import {Row, Col, Popover, PopoverBody} from 'reactstrap';
import '../../style/InstallationScheduling.css';
import InstallationSchedulingPerPackage from './InstallationSchedulingPerPackage.jsx';
import {orderTexts} from '../../../../constants/ordersConstants';
import {setDialogContent, setDialogOpenFlag} from '../../../../actions/dialog/dialogActions';
class InstallationSchedulingForPackages extends Component {
constructor(props) {
super(props);
this.state = {
popoverOpen: false
}
this.toggle = this.toggle.bind(this);
this.setContentDialog = this.setContentDialog.bind(this);
this.openDialogInstallContent = this.openDialogInstallContent.bind(this);
}
setContentDialog(installationParams) {
return {
class: 'installation-scheduling',
hasCloseIcon: true,
header: orderTexts.labels.INSTALLATION_SCHEDULE_HEADER,
TagName: InstallationSchedulingPerPackage,
params: {installationParams, openDialogInstallContent: this.openDialogInstallContent}
};
}
openDialogInstallContent(installationParams) {
const dialogContent = this.setContentDialog(installationParams);
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
render() {
const {orderPackage, areComponentsDisabled, isInstallationSet, isInstallationInPackage, noOfPackages, installCompanies, isComponentDisabled, earliestInstallDate, confirmationDates, areAllShippingDatesConfirmed} = this.props;
const idOrder = orderPackage ? orderPackage.idOrder : 0;
const idOrderPackagePair = orderPackage ? idOrder + '-' + orderPackage.idPackage : '';
const acceptedDate = {};
const proposedDate = {};
const installationCompany = {};
const availableCompanies = {};
const selectedCompanies = {};
const isPreliminaryInstallationDate = {};
if(confirmationDates && confirmationDates[idOrderPackagePair]) {
const packageDates = confirmationDates[idOrderPackagePair];
const accepted = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'accepted'});
acceptedDate[idOrderPackagePair] = accepted ? moment(accepted).format('Do MMM, YYYY') : '';
const proposed = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'proposed'});
proposedDate[idOrderPackagePair] = proposed ? moment(proposed).format('Do MMM, YYYY') : '';
}
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) {
installationCompany[idOrderPackagePair] = selectedCompanies[idOrderPackagePair];
}
}
isPreliminaryInstallationDate[idOrderPackagePair] = areAllShippingDatesConfirmed && orderPackage.idOrder in areAllShippingDatesConfirmed ? !areAllShippingDatesConfirmed[orderPackage.idOrder] : true;
const isSchedulingDisabled = (idOrder in areComponentsDisabled && areComponentsDisabled[idOrder]) || (isInstallationSet && !isInstallationSet[idOrderPackagePair]);
const installationParams = {
idOrder,
idOrderPackagePair,
orderPackage,
installCompanies,
isComponentDisabled,
earliestInstallDate,
confirmationDates,
isThisComponentDisabled: areComponentsDisabled[idOrder],
availableCompanies,
selectedCompanies,
installationCompany,
isSchedulingDisabled,
isInstallationSet,
isPreliminaryInstallationDate,
acceptedDate,
proposedDate
};
return (
<span id="installation-scheduler-container">
{ noOfPackages === 1
? this.openDialogInstallContent(installationParams)
: <Row className="package-installation-row">
<Col xl="7" id={"package-name-" + orderPackage.idOrder + '-' + orderPackage.idPackage}>
{orderPackage.packageName}
</Col>
<Col xl="5">
<div className="subtitle">
{
isInstallationInPackage && isInstallationInPackage[idOrderPackagePair] === false
? <span>{orderTexts.labels.INSTALLATION_NOT_REQUIRED}</span>
: isPreliminaryInstallationDate[idOrderPackagePair]
? <span>{orderTexts.labels.PRELIMINARY_INSTALLATION_DATE_LABEL}: </span>
: <span>{orderTexts.labels.INSTALLATION_DATE}: </span>
}
</div>
<div id={"installation-scheduler-container-" + idOrderPackagePair} className="install-date-text">
{
isSchedulingDisabled
? acceptedDate[idOrderPackagePair]
? acceptedDate[idOrderPackagePair]
: proposedDate[idOrderPackagePair]
? proposedDate[idOrderPackagePair]
: (isInstallationInPackage[idOrderPackagePair] && !proposedDate[idOrderPackagePair] && !acceptedDate[idOrderPackagePair]) && orderTexts.labels.NOT_SET
: <span className="check-installation-dates"
onClick={() => this.openDialogInstallContent(installationParams)}>
{
acceptedDate[idOrderPackagePair]
? acceptedDate[idOrderPackagePair]
: proposedDate[idOrderPackagePair]
? proposedDate[idOrderPackagePair]
: (isInstallationInPackage[idOrderPackagePair] && !proposedDate[idOrderPackagePair] && !acceptedDate[idOrderPackagePair]) && orderTexts.labels.NOT_SET
}
</span>
}
{
isInstallationInPackage[idOrderPackagePair] &&
<span>
<i className="scheduled-date-btn fa fa-info-circle"
id={'scheduled-date-' + idOrderPackagePair}
onClick={this.toggle}></i>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'scheduled-date-' + idOrderPackagePair}
container={"installation-scheduler-container-" + idOrderPackagePair}
className="scheduled-date-popover"
toggle={this.toggle}>
<PopoverBody>
{
isPreliminaryInstallationDate[idOrderPackagePair]
? acceptedDate[idOrderPackagePair]
? orderTexts.labels.PRELIMINARY_INSTALLATION_DATE
: orderTexts.labels.INSTALLATION_DATE_NOT_SET
: acceptedDate[idOrderPackagePair]
? orderTexts.labels.INSTALLATION_DATE_ACCEPTED
: orderTexts.labels.INSTALLATION_DATE_NOT_SET
}
</PopoverBody>
</Popover>
</span>
}
</div>
</Col>
</Row>
}
</span>
);
}
}
const mapStateToProps = (state) => ({
isNextStepWanted: state.processReducer.isNextStepWanted,
isComponentDisabled: state.processReducer.isComponentDisabled,
earliestInstallDate: state.processReducer.earliestInstallDate,
confirmationDates: state.processReducer.confirmationDates,
installCompanies: state.processReducer.installCompanies
});
export default connect(mapStateToProps)(InstallationSchedulingForPackages);

View File

@@ -0,0 +1,161 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import moment from 'moment';
import {Col, Button, Popover, PopoverBody} from 'reactstrap';
import '../../style/InstallationScheduling.css';
import {API_SERVER} from '../../../../config';
import FileDownloader from '../../../../helpers/FileDownloader';
import InstallationSchedulingDatesPerPackage from './InstallationSchedulingDatesPerPackage.jsx';
import {updateInstallationDate} from '../../../../actions/orders/processActions';
import {orderTexts} from '../../../../constants/ordersConstants';
const fileHandler = new FileDownloader();
const fileType = 'installationProtocol';
const status = 'proposed';
class InstallationSchedulingPerPackage extends Component {
constructor(props) {
super(props);
this.state = {
startDate: undefined
};
this.toggle = this.toggle.bind(this);
this.handleDateChange = this.handleDateChange.bind(this);
this.handleSelect = this.handleSelect.bind(this);
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
handleDateChange(startDate) {
this.setState({startDate});
}
handleSelect(date, idOrder, idPackage) {
const newDate = new Date(moment(date).toISOString());
this.handleDateChange(date);
this.toggle();
if(Date.parse(newDate)) {
const updateDate = moment(date).format('YYYY-MM-DD');
this.props.dispatch(updateInstallationDate(idOrder, idPackage, updateDate, status, this.handleDateChange));
}
}
downloadDocument(document){
const fileUrl = `${API_SERVER}/utils/api/downloadFile?idDocument=${document.idDocument}&fileName=${document.documentName}.${document.extension}&fileType=${fileType}`
const fileName = document.documentName + '.' + document.extension;
fileHandler.download(fileUrl, fileName);
}
render() {
const acceptedDate = {};
const proposedDate = {};
const {
idOrder,
idOrderPackagePair,
orderPackage,
earliestInstallDate,
isThisComponentDisabled,
isPreliminaryInstallationDate,
installationCompany,
isInstallationSet
} = this.props.params.installationParams;
const {confirmationDates} = this.props;
const earliestInstallationDate = earliestInstallDate && moment(earliestInstallDate[idOrder]);
if(confirmationDates && confirmationDates[idOrderPackagePair]) {
const packageDates = confirmationDates[idOrderPackagePair];
const accepted = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'accepted'});
acceptedDate[idOrderPackagePair] = accepted ? moment(accepted).format('Do MMM, YYYY') : '';
const proposed = Object.keys(packageDates).find(installDate => {return packageDates[installDate].lastStatus === 'proposed'});
proposedDate[idOrderPackagePair] = proposed ? moment(proposed).format('Do MMM, YYYY') : '';
}
return (
<fieldset disabled={isThisComponentDisabled} className="set-installation-dates">
<Col>
<div className="subtitle">
{
isPreliminaryInstallationDate[idOrderPackagePair]
? <h6>{orderTexts.labels.PRELIMINARY_INSTALLATION_DATE_LABEL}: </h6>
: <h6>{orderTexts.labels.INSTALLATION_DATE}: </h6>
}
</div>
<div id={"installation-scheduler-container-" + idOrderPackagePair} className="install-date-text">
{
acceptedDate[idOrderPackagePair]
? acceptedDate[idOrderPackagePair]
: proposedDate[idOrderPackagePair]
? proposedDate[idOrderPackagePair]
: (!proposedDate[idOrderPackagePair] && !acceptedDate[idOrderPackagePair]) && orderTexts.labels.NOT_SET
}
</div>
</Col>
<Col>
<div className="subtitle"><h6>{orderTexts.labels.INSTALL_COMPANY}:</h6></div>
<span id={"installation-company-name-" + idOrderPackagePair}>
{ isInstallationSet[idOrderPackagePair] ? installationCompany[idOrderPackagePair].name : <span>{orderTexts.labels.INSTALL_COMPANY_NOT_SET}</span>}
</span>
</Col>
<Col>
<div className="subtitle"><h6>{orderTexts.labels.EARLIEST_INSTALLATION}:</h6></div>
<span id={"earliest-installation-date-" + idOrderPackagePair}>
{(earliestInstallDate && earliestInstallDate[idOrder])
? moment(earliestInstallDate[idOrder]).format('Do MMM, YYYY')
: '-'}
</span>
</Col>
<Col>
<div className="subtitle"><h6>{orderTexts.labels.PROPOSED_DATES}</h6></div>
{
confirmationDates && confirmationDates[idOrderPackagePair]
&& Object.keys(confirmationDates[idOrderPackagePair]).map((confirmationDate) =>
<InstallationSchedulingDatesPerPackage
key={'order-package-installation-dates-' + confirmationDate}
minDate={earliestInstallationDate}
confirmationDate={confirmationDate}
dateInfo={confirmationDates[idOrderPackagePair][confirmationDate]}
orderPackage={orderPackage}
openDialogInstallContent={this.props.params.openDialogInstallContent}
installationParams={this.props.params.installationParams}
isComponentDisabled={isThisComponentDisabled}/>)
}
</Col>
<Col id={"installation-scheduling-propose-date-" + idOrderPackagePair}>
<Button id={"propose-new-installation-date-" + idOrderPackagePair} disabled={isThisComponentDisabled} className="propose-new-date-btn" onClick={this.toggle}>Add optional date</Button>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={"propose-new-installation-date-" + idOrderPackagePair}
container={"installation-scheduling-propose-date-" + idOrderPackagePair}
className="dates-info-popover"
toggle={this.toggle}>
<PopoverBody>
<DatePicker
inline
id={"installation-date-propose-" + idOrderPackagePair}
selected={this.state.startDate}
onSelect={(date) => this.handleSelect(date, orderPackage.idOrder, orderPackage.idPackage)}
minDate={earliestInstallationDate}
dateFormat="Do MMM, YYYY"
placeholderText="Choose a date" />
</PopoverBody>
</Popover>
</Col>
</fieldset>
);
}
}
const mapStateToProps = (state) => ({
confirmationDates: state.processReducer.confirmationDates
});
export default connect(mapStateToProps)(InstallationSchedulingPerPackage);

View File

@@ -0,0 +1,142 @@
import React, {Component} from 'react';
import {Row, Col, Container, Popover, PopoverBody, PopoverHeader} from 'reactstrap';
import {API_SERVER} from '../../../../config';
import FileDownloader from '../../../../helpers/FileDownloader';
import {orderTexts} from '../../../../constants/ordersConstants';
const fileHandler = new FileDownloader();
class PackageInfo extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
popoverOpen: false
};
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
downloadDocument(document){
const fileUrl = `${API_SERVER}/utils/api/downloadFile?idDocument=${document.idDocument}&fileName=${document.documentName}.${document.extension}&fileType=${document.documentTypeName}`
const fileName = document.documentName + '.' + document.extension;
fileHandler.download(fileUrl, fileName);
}
render() {
const {orderPackage, onViewChange} = this.props;
const shouldShowPriceInfo = orderPackage.packageRecuringPrice > 0 || orderPackage.packageServicePrice > 0;
return (
<Container fluid={true} id="package-info" className="order-package-info">
<Row>
<Col xl="3">
<div className="subtitle">
<h6>
{orderTexts.labels.PACKAGE_PRICE}: {' '}
{ shouldShowPriceInfo &&
<i className="price-info-btn fa fa-info-circle"
id={'price-info-' + orderPackage.idPackage}
onClick={this.toggle}></i>
}
</h6>
</div>
<div>
{orderTexts.labels.TOTAL_DELVIERY_PRICE}: {' '}
{(orderPackage.units * orderPackage.packageFixedPrice).toLocaleString() + ' ' + orderPackage.packageCurrency.currency}
</div>
{
shouldShowPriceInfo &&
<div>
{orderTexts.labels.TOTAL_RECURRENT_PRICE}:{' '}
{(orderPackage.units * (orderPackage.packageRecuringPrice + orderPackage.packageServicePrice)).toLocaleString()} {orderPackage.packageCurrency.currency}
</div>
}
{ shouldShowPriceInfo &&
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'price-info-' + orderPackage.idPackage}
container={'price-info-' + orderPackage.idPackage}
className="price-info-popover"
toggle={this.toggle}>
<PopoverHeader>{orderPackage.paymentType}</PopoverHeader>
<PopoverBody>
<div>
{ orderPackage.packageRecuringPrice > 0 &&
<div className="package-price-recurrent">
<span className="price-info-title">{orderTexts.labels.RECURRENT_PRICE}: </span>
{(orderPackage.units * orderPackage.packageRecuringPrice).toLocaleString()} {orderPackage.packageCurrency.currency} / {orderPackage.periodUnit}
{
orderPackage.packagePayPeriod > 0 &&
<span>
for {orderPackage.packagePayPeriod} {orderPackage.periodUnit}
</span>
}
</div>
}
{ orderPackage.packageServicePrice > 0 &&
<div className="services-price-recurrent">
<span className="price-info-title">{orderTexts.labels.SERVICES_PRICE}: </span>
{(orderPackage.units * orderPackage.packageServicePrice).toLocaleString()} {orderPackage.packageCurrency.currency} / {orderPackage.periodUnit}
{
orderPackage.servicesContractPeriod > 0 &&
<span>
for {orderPackage.servicesContractPeriod} {orderPackage.periodUnit}
</span>
}
<span> {orderTexts.labels.EXTEND} {orderPackage.periodUnit} (Max {orderPackage.maxContractPeriod} {orderPackage.periodUnit})</span>
</div>
}
</div>
</PopoverBody>
</Popover>
}
</Col>
{
orderPackage.options.length > 0 &&
<Col xl="3">
<div className="subtitle"><h6>{orderTexts.labels.OPTIONS}:</h6></div>
{
orderPackage.options.map((option, index) => <div key={'option-'+index}>
{option.groupName}: {option.packageName}
</div>)
}
</Col>
}
{
orderPackage.additionalPackages.length > 0 &&
<Col xl="3">
<div className="subtitle"><h6>{orderTexts.labels.ADDITIONAL_PACKAGES}:</h6></div>
{
orderPackage.additionalPackages.map((additional, index) => <div key={'additional-'+index}>
{additional.packageName}
</div>)
}
</Col>
}
{
orderPackage.documents &&
<Col xl="2">
<div className="subtitle"><h6>{orderTexts.labels.DOCUMENTS}:</h6></div>
{
orderPackage.documents.length > 0 ?
<div className="link-to-docs" onClick={()=>onViewChange('documents')}>{orderTexts.buttons.SEE_DOCUMENTS}</div>:
<span>{orderTexts.labels.NO_DOCUMENTS}</span>
}
</Col>
}
</Row>
</Container>
);
}
}
export default PackageInfo;

View File

@@ -0,0 +1,16 @@
import React, {Component} from 'react';
import {Link} from 'react-router-dom';
class PackageName extends Component {
render(){
const {orderPackage, idCommercialLead} = this.props.params;
return (
<div>
<Link to={'/co-market/' + idCommercialLead + '/' + orderPackage.idPackage } className="package-name-link">{orderPackage.units + ' x ' + orderPackage.packageName}</Link>
</div>
);
}
}
export default PackageName;

View File

@@ -0,0 +1,23 @@
import React, {Component} from 'react';
import {Row, Col} from 'reactstrap';
import WiaasBox from '../../../../mainComponents/box/WiaasBox.jsx';
import PackageInfo from './PackageInfo.jsx';
import PackageName from './PackageName.jsx';
class ProcessPackage extends Component {
render() {
const {orderPackage, idCommercialLead} = this.props;
return (
<WiaasBox customHeader={PackageName} customHeaderParams={{orderPackage, idCommercialLead}} className="order-package">
<Row>
<Col xl="12" lg="12" md="12" xs="12">
<PackageInfo onViewChange={this.props.onViewChange} orderPackage={orderPackage}/>
</Col>
</Row>
</WiaasBox>
);
}
}
export default ProcessPackage;

View File

@@ -0,0 +1,30 @@
import React, {Component} from 'react';
import {Input} from 'reactstrap';
import {orderTexts} from '../../../../constants/ordersConstants';
class AcceptanceDeclineReason extends Component {
handleEditorChange = (e) => {
const reason = e.target.value;
this.props.params.onEditorChange(reason);
}
render() {
const {actionType} = this.props.params;
return (
<div>
{
actionType === 'accept'
? <div>{orderTexts.labels.ACCEPT_INSTALLATION_TEXT}</div>
: <div>
<div>{orderTexts.labels.DECLINE_REASON_TEXT}</div>
<Input onChange={this.handleEditorChange} type="textarea" name="acceptance-decline-reason" id="installation-declined-reason" />
</div>
}
</div>
);
}
}
export default AcceptanceDeclineReason;

View File

@@ -0,0 +1,193 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Dropzone from 'react-dropzone';
import {Row, Col, Button} from 'reactstrap';
import {fetchCustomerAcceptance, uploadAcceptance, acceptDeclineInstallation, badFile} from '../../../../actions/orders/customerAcceptanceActions';
import {setDialogContent, setDialogOpenFlag} from '../../../../actions/dialog/dialogActions';
import AcceptanceDeclineReason from './AcceptanceDeclineReason.jsx';
import {API_SERVER} from '../../../../config';
import FileDownloader from '../../../../helpers/FileDownloader';
import {orderTexts} from '../../../../constants/ordersConstants';
import '../../style/CustomerAcceptance.css';
const fileHandler = new FileDownloader();
class CustomerAcceptance extends Component {
constructor(props){
super(props);
this.state = {
reason : '',
actionType : ''
}
this.acceptDeclineInstallation = this.acceptDeclineInstallation.bind(this);
this.onEditorChange = this.onEditorChange.bind(this);
}
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);
}
uploadFile(idOrder, acceptedFiles, rejectedFiles) {
if(acceptedFiles && acceptedFiles.length){
const file = acceptedFiles[0];
this.props.dispatch(uploadAcceptance(idOrder, file));
}
if(rejectedFiles && rejectedFiles.length) {
this.props.dispatch(badFile());
}
}
getAcceptanceStatusClass(acceptance, daysDiff) {
const statuses = {
0 : 'waiting',
1 : 'accepted',
'-1' : 'declined'
};
if(acceptance === 0 && daysDiff <= 0){
return 'danger';
} else if(acceptance === 0 && daysDiff <= 3) {
return 'warning';
}else{
return statuses[acceptance];
}
}
acceptDeclineInstallation() {
const {idOrder} = this.props.step;
const {actionType, reason} = this.state;
this.props.dispatch(acceptDeclineInstallation(idOrder, actionType, reason));
this.setState({reason: ''});
}
getAcceptanceMessage(customerAcceptance){
const messages = {
0 : orderTexts.labels.NOT_ACCEPTED + (customerAcceptance.acceptanceDueDate || orderTexts.labels.NOT_SET),
1 : orderTexts.labels.ACCEPTED,
'-1' : orderTexts.labels.DECLINED
}
return messages[customerAcceptance.customerAccepted];
}
onEditorChange(reason) {
this.setState({reason});
}
openDialog(actionType) {
const dialogContent = this.getAcceptanceDialog(actionType);
this.setState({actionType});
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
getAcceptanceDialog(actionType) {
return {
buttons: [
{
color: 'success',
action: this.acceptDeclineInstallation,
name: orderTexts.buttons.SEND,
id: 'confirm-acceptance'
}, {
color: 'secondary',
name: orderTexts.buttons.CANCEL,
id: 'cancel-acceptance'
}
],
header: orderTexts.labels.ACCEPTANCE_HEADER,
TagName: AcceptanceDeclineReason,
params: {onEditorChange: this.onEditorChange, actionType}
};
}
componentDidMount(){
const {idOrder} = this.props.step;
this.props.dispatch(fetchCustomerAcceptance(idOrder));
}
render() {
const {step} = this.props;
const customerAcceptance = this.props.customerAcceptance || null;
return (
<div id="customer-acceptance" className="validate-questionnaire">
{
customerAcceptance &&
<Row>
<Col className="aceeptance-message">
{this.getAcceptanceMessage(customerAcceptance)} <div className={'status-icon ' + this.getAcceptanceStatusClass(customerAcceptance.customerAccepted, customerAcceptance.daysDiff)}></div>
</Col>
</Row>
}
{
(customerAcceptance && customerAcceptance.customerAccepted === -1) &&
<Row>
<Col>{orderTexts.labels.REASON}: {customerAcceptance.customerDeclineReason}</Col>
</Row>
}
<Row className="acceptance-docs">
<Col xl="4" lg="5" md="4">
<Dropzone className="upload-file-drop-zone"
multiple={false}
accept=".pdf,.docx,.doc,.xlsx,.xls,.odt,.ods,.jpg,.png,.jpeg"
activeClassName="upload-file-accept"
onDrop={(acceptedFiles, rejectedFiles)=>{this.uploadFile(step.idOrder, acceptedFiles, rejectedFiles)}}>
<h5 className="drop-zone-text">{orderTexts.labels.UPLOAD_ACCEPTANCE_LABEL}</h5>
</Dropzone>
</Col>
<Col xl="4" lg="7" md="8">
{
(customerAcceptance && customerAcceptance.acceptanceDocuments && customerAcceptance.acceptanceDocuments.length > 0) &&
<div>
{
customerAcceptance.acceptanceDocuments.map(document => <div key={'acceptance-documnet-' + document.idDocument}>
<span className="document-link"
onClick={() => {this.downloadDocument(document)}}>
<i className={'fa fa-file'}></i> {document.documentName} ({document.extension})
</span>
<span className="document-status">
{document.validation} <div className={'status-icon ' + document.validation}></div>
</span>
</div>)
}
</div>
}
{
(customerAcceptance && !customerAcceptance.acceptanceDocuments) &&
<div>
{orderTexts.labels.NO_DOCUMENTS_UPLOADED}
</div>
}
</Col>
</Row>
<Row>
<Col className="acceptance-label" xl="12" lg="12" md="12" xs="12">
{orderTexts.labels.ACCEPTANCE_LABEL}
</Col>
<Col lg="7">
<Button onClick={()=>{this.openDialog('accept')}}
id="acceptance-accept"
color="secondary"
className="acceptance-button acceptance-accept">{orderTexts.buttons.ACCEPT_INSTALLATION}</Button>
<Button onClick={()=>{this.openDialog('decline')}}
id="acceptance-decline"
color="secondary"
className="acceptance-button acceptance-decline">{orderTexts.buttons.DECLINE_INSTALLATION}</Button>
</Col>
</Row>
</div>
);
}
}
const mapStateToProps = (state) => ({
customerAcceptance: state.processReducer.customerAcceptance
});
export default connect(mapStateToProps)(CustomerAcceptance);

View File

@@ -0,0 +1,54 @@
import React, {Component} from 'react';
import {Row, Col} from 'reactstrap';
import ProcessStep from './ProcessStep.jsx';
import {orderTexts} from '../../../../constants/ordersConstants';
const completedOrdersStatuses = ['production', 'end-of-life'];
class OrderProcess extends Component {
isStepVisible(step) {
return (step.status === 'in-progress' || step.status === 'done') && step.isVisibleForCustomer === 1;
}
render() {
const {orderProcess, orderStatus} = this.props;
const visibleSteps = (orderProcess && orderProcess.steps) ? orderProcess.steps.filter(this.isStepVisible) : [];
if(orderProcess && completedOrdersStatuses.find((status) => {return status === orderStatus;})) {
const processCompleted = {
shortDesc: orderTexts.labels.COMPLETED,
status: 'done',
isVisibleForCustomer: 1,
actualDate: visibleSteps[0].actualDate
};
if(visibleSteps) {
visibleSteps.unshift(processCompleted);
}
}
return (
<div className="order-process">
{
(!orderProcess || !orderProcess.steps) &&
<Row>
<Col xl="12" lg="12" md="12" xs="12">
<div className="no-process-info">{orderTexts.labels.WILL_BE_PROCESS}</div>
</Col>
</Row>
}
{
(orderProcess && orderProcess.steps) &&
<Row>
<Col xl="12" lg="12" md="12" xs="12" className="order-package-process">
{
visibleSteps.map((step, index) => <ProcessStep isStepVisible={this.isStepVisible} stepNumber={visibleSteps.length - index} step={step} key={'step-' + step.idProcess + '-' + step.idProcessStep}/>)
}
</Col>
</Row>
}
</div>
);
}
}
export default OrderProcess;

View File

@@ -0,0 +1,76 @@
import React, {Component} from 'react';
import {Row, Col, Container} from 'reactstrap';
import WiaasBox from '../../../../mainComponents/box/WiaasBox.jsx';
import ValidateQuestionnaire from './ValidateQuestionnaire.jsx';
import CustomerAcceptance from './CustomerAcceptance.jsx';
const stepActions = {
'validate-questionnaire' : ValidateQuestionnaire,
'customer-acceptance' : CustomerAcceptance
}
class OrderStep extends Component {
isActiveStep(status) {
return status === 'in-progress' ;
}
getStepTitle(step, stepNumber) {
return stepNumber + '. ' + step.shortDesc;
}
getDayFromActual(step) {
const date = step.actualDate || step.now;
const dateParts = date.split(' ');
return dateParts[0];
}
getMonthFromActual(step) {
const date = step.actualDate || step.now;
const dateParts = date.split(' ');
const dateParts2 = dateParts[1].split(',');
return dateParts2[0];
}
render() {
const {step, stepNumber, isStepVisible} = this.props;
const TagName = step.actionCode !== 'manual' && stepActions[step.actionCode] ? stepActions[step.actionCode] : null;
return (
<Container fluid={true} className="order-step">
{
isStepVisible(step) &&
<Row className="step-layer">
<Col xl="1" lg="1" md="2" xs="2" className="step-circle-layer">
<div className="step-line"></div>
<div className={'step-circle ' + step.status}>
{!this.isActiveStep(step.status) &&
<span>
<div>{this.getDayFromActual(step)}</div>
<div>{this.getMonthFromActual(step)}</div>
</span>
}
</div>
</Col>
<Col xl="11" lg="11" md="10" xs="10" className="step-box-layer">
<WiaasBox isContentVisible={this.isActiveStep(step.status)} className={'step-box'} mainTitle={this.getStepTitle(step, stepNumber)}>
{
(this.isActiveStep(step.status) && TagName) &&
<Col>
{
(TagName && this.isActiveStep(step.status)) && <TagName step={step}/>
}
</Col>
}
</WiaasBox>
</Col>
</Row>
}
</Container>
);
}
}
export default OrderStep;

View File

@@ -0,0 +1,46 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {fetchCustomerDocuments, fetchValidationComments} from '../../../../actions/orders/processActions';
import ValidateQuestionnaireItem from './ValidateQuestionnaireItem.jsx';
import '../../style/ValidateQuestionnaire.css';
class ValidateQuestionnaire extends Component {
componentDidMount(){
const {idOrder, idProcessStep} = this.props.step;
this.props.dispatch(fetchCustomerDocuments(idOrder, 'orderQuestionaire'));
this.props.dispatch(fetchValidationComments(idOrder, idProcessStep, 'invalidQuestionnaireComment'));
}
findById(orderPackage, idOrderPackagePair){
const idPackage = idOrderPackagePair.split('-')[1];
return orderPackage.idPackage === parseInt(idPackage, 10);
}
render() {
const {customerDocuments, validationComments, orderPackages} = this.props;
return (
<div id="validate-questionnaire" className="validate-questionnaire">
{
customerDocuments &&
Object.keys(customerDocuments).map((idOrderPackagePair) =>
<ValidateQuestionnaireItem
customerDocuments={customerDocuments[idOrderPackagePair]}
validationComments={validationComments && validationComments[idOrderPackagePair] ? validationComments[idOrderPackagePair] : []}
orderPackage={orderPackages.find((orderPackage)=>{ return this.findById(orderPackage, idOrderPackagePair)})}
key={'validate-questionnaire-' + idOrderPackagePair}/>
)
}
</div>
);
}
}
const mapStateToProps = (state) => ({
customerDocuments: state.processReducer.customerDocuments,
validationComments: state.processReducer.validationComments,
orderPackages: state.processReducer.orderInfo.packages
});
export default connect(mapStateToProps)(ValidateQuestionnaire);

View File

@@ -0,0 +1,99 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Dropzone from 'react-dropzone';
import {Row, Col} from 'reactstrap';
import {reUploadOrderDocument, badFile} from '../../../../actions/orders/processActions';
import {API_SERVER} from '../../../../config';
import FileDownloader from '../../../../helpers/FileDownloader';
import {orderTexts} from '../../../../constants/ordersConstants';
const fileHandler = new FileDownloader();
class ValidateQuestionnaireItem 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);
}
uploadFile(idPackage, idOrder, idDocument,acceptedFiles, rejectedFiles) {
if(acceptedFiles && acceptedFiles.length){
const file = acceptedFiles[0];
this.props.dispatch(reUploadOrderDocument(idPackage, idOrder, idDocument, file));
}
if(rejectedFiles && rejectedFiles.length) {
this.props.dispatch(badFile());
}
}
render() {
const {customerDocuments, validationComments, orderPackage} = this.props;
return (
<div id="validate-questionnaire" className="validate-questionnaire">
{
customerDocuments &&
<div>
{orderPackage.packageName}
{
customerDocuments.map(document => <div key={'package-document-' + document.idDocument}>
{
document.validation === 'invalid'
? <div className="package-document">
<Row>
<Col xl="7" lg="8">
<div>
<span className="document-link"
onClick={() => {this.downloadDocument(document)}}>
<i className={'fa fa-file'}></i> {document.documentName} ({document.extension}) {' '}
</span>
<span className="document-status">
{document.validation.replace(/-/g,' ')} <div className={'status-icon ' + document.validation}></div>
</span>
</div>
{
(validationComments && validationComments.length > 0) &&
<div>
{validationComments.map((comment, key) => <div key={'step-comment-' + document.idDocument + '-' + key} className="step-comment">
<div>{comment.user} - {comment.addDate}</div>
<div>{comment.comment}</div>
</div>)}
</div>
}
</Col>
<Col xl="5">
<Dropzone className="upload-file-drop-zone"
multiple={false}
accept=".pdf,.docx,.doc,.xlsx,.xls,.odt,.ods"
activeClassName="upload-file-accept"
onDrop={(acceptedFiles, rejectedFiles)=>{this.uploadFile(document.idPackage, document.idOrder, document.idDocument, acceptedFiles, rejectedFiles)}}>
<h5 className="drop-zone-text">{orderTexts.labels.SELECT_OR_DROP}</h5>
</Dropzone>
</Col>
</Row>
</div>
: <div className="package-document">
<Row>
<Col>
<span className="document-link"
onClick={() => {this.downloadDocument(document)}}>
<i className={'fa fa-file'}></i> {document.documentName} ({document.extension}) {' '}
</span>
<span className="document-status">
{document.validation.replace(/-/g,' ')} <div className={'status-icon ' + document.validation}></div>
</span>
</Col>
</Row>
</div>
}
</div>)
}
</div>
}
</div>
);
}
}
export default connect()(ValidateQuestionnaireItem);

View File

@@ -0,0 +1,66 @@
@import '../../../styleConstants.scss';
#customer-acceptance {
padding: 1rem 0;
.waiting {
background: $pending-status-color;
}
.accepted {
background: $validated-status-color;
}
.declined {
background: $declined-stattus-color;
}
.danger {
background: $danger;
}
.warning {
background: $warning;
}
.acceptance-button {
margin-top: 1rem;
cursor: pointer;
background: $whiteColor;
}
.acceptance-accept {
color: $production-status-color;
}
.acceptance-decline {
margin-left: 0.5rem;
color: $canceled-status-color;
.package-document {
margin-top: 1rem;
}
}
.acceptance-docs {
margin-top: 1rem;
}
.acceptance-label {
margin-top: 1rem;
font-weight: $font-weight;
}
@media all and (max-width: 768px) {
.aceeptance-message {
font-size: $font-size-small
}
}
@media all and (max-width: 480px) {
.acceptance-decline {
margin-left: 0;
}
}
}

View File

@@ -0,0 +1,112 @@
@import '../../../styleConstants.scss';
.installation-scheduling-dates-container {
padding-bottom: 1rem;
.confirmation-dates-buttons {
float: right;
.install-btn-accept {
color: $production-status-color;
}
.install-btn-decline {
color: $canceled-status-color;
}
.install-btn-remove {
color: $warm-grey;
}
}
.installation-button {
width: 5rem;
margin-left: 1rem;
height: 2rem;
font-size: $font-size-small;
cursor: pointer;
background-color: $whiteColor;
font-weight: $font-weight;
}
.dates-info-btn {
margin-left: 1rem;
cursor: pointer;
float: right;
}
.dates-info-popover {
max-width: 50rem;
}
.status-icon-installation-dates {
padding-right: 3px;
}
.installation-date-proposed {
cursor: default;
}
}
.installation-scheduling-dates-container:hover {
background-color: $hoverColor;
}
.dates-info-popover {
.react-datepicker {
font-family: $ProximaNova-font;
border: none;
border-radius: unset;
}
}
#installation-scheduler-container {
.document-link {
cursor: pointer;
word-wrap: break-word;
font-size: $font-size-small;
display: inline;
}
.set-installation-dates {
background: $whiteColor;
}
.check-installation-dates {
cursor: pointer;
color: $blueColor;
font-weight: $font-weight;
}
.subtitle {
padding-right: 0.5rem;
display: inline-block;
}
.install-date-text {
display: inline-block;
padding-right: 0.5rem;
}
}
.subtitle {
padding-top: 0.5rem;
}
.order-package-row {
padding: 0.5rem;
}
.package-installation-row {
padding: 0.5rem;
}
.scheduled-date-btn {
margin-left: 0.7rem;
cursor: pointer;
float: right;
}
.scheduled-date-popover {
max-width: 50rem;
}

View File

@@ -0,0 +1,128 @@
@import '../../../styleConstants.scss';
$borderWidth: 3px;
#orders-list-container {
.orders-tab {
cursor: pointer;
}
.order-central-row {
border-radius: 1.5px;
}
.order-border-open {
border-left: $borderWidth $open-status-color solid;
}
.order-border-in-progress {
border-left: $borderWidth $in-progress-status-color solid;
}
.order-border-production {
border-left: $borderWidth $production-status-color solid;
}
.order-border-end-of-life {
border-left: $borderWidth $end-of-life-status-color solid;
}
.order-border-canceled {
border-left: $borderWidth $canceled-status-color solid;
}
.cl-photo{
width: 2rem;
height: 2rem;
}
.order-number {
display: inline-block;
padding-left: 1rem;
bottom: 0;
}
.package-photo {
padding-left: 1rem;
width: 20%;
}
.package-info-col {
width: 100%;
}
.actions-button {
background-color: $whiteColor;
color: $darkGreyColor;
cursor: pointer;
}
.actions-button:hover{
background: rgba(113, 194, 191, 0.5);
}
.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;
}
.production {
background: $production-status-color;
}
.order-item-cl {
display: inline-flex;
}
.cl-contact-name {
font-weight: $font-weight;
color: $darkGreyColor;
font-size: $font-size-small;
}
.cl-name {
font-weight: unset;
font-size: $font-size-msmall;
color: $warmGreyColor;
}
.cl-details {
margin-left: 1rem;
}
.package-name {
display: inline-block;
padding-left: 1rem;
bottom: 0;
font-size: $font-size-small;
font-weight: $font-weight;
width: 80%;
}
.order-details-container {
padding-left: 1rem;
}
.toggle-view-packages {
cursor: pointer;
}
.actions-link {
color: $darkGreyColor;
text-decoration: none;
}
.actions-item {
cursor: pointer;
}
}

View File

@@ -0,0 +1,3 @@
.view-all-orders-text {
padding-left: 0.3rem;
}

View File

@@ -0,0 +1,366 @@
@import '../../../styleConstants.scss';
$border-width: 5px;
$link-line-height: 1.5rem;
#order-info {
.subtitle {
margin-top: 0.5rem;
}
.info-color {
margin-top: 0.2rem;
padding-left: 0.2rem;
}
.no-process {
background: $open-status-color;
}
.processing {
border-left: $border-width $processing-status-color solid;
}
.canceled {
border-left: $border-width $canceled-status-color solid;
}
.production {
border-left: $border-width $production-status-color solid;
}
.end-of-life {
border-left: $border-width $end-of-life-status-color solid;
}
.wiaas-box-container {
padding: 0;
}
.components-link {
position: relative;
height: $link-line-height;
.link-line {
position: absolute;
background: $borderColor;
width: 3px;
top: 0;
left: 49%;
height: $link-line-height;
z-index: 0;
}
}
.package-name-link {
font-size: $font-size-big;
font-weight: $font-weight;
text-align: left;
color: $title-color;
}
}
.order-process{
.no-process-info {
background: $whiteColor;
padding: 1rem;
font-size: $font-size-normal;
font-weight: $font-weight;
}
}
.order-step{
.wiaas-box-container {
padding: 0;
}
}
.step-circle-layer {
text-align: center;
}
.step-circle {
z-index: 1;
margin-top: $link-line-height;
position: relative;
border-radius: 50%;
text-align: center;
width: 3rem;
height: 3rem;
color: $whiteColor;
line-height: 1rem;
padding: 0.5rem;
display: inline-block;
font-weight: $font-weight;
font-size: $font-size-small;
}
.step-line{
position: absolute;
background: $borderColor;
width: 2%;
top: 0;
left: 49%;
height: 100%;
z-index: 0;
}
.order-step:last-child .step-line{
height: 50%;
}
.step-box-layer {
margin-top: $link-line-height;
position: relative;
}
.step-box {
background: $whiteColor;
border-radius: 1.5px;
.wiaas-main-title {
font-size: $font-size-small;
}
}
.end-of-life {
background: $end-of-life-status-color;
}
.canceled {
background: $canceled-status-color;
}
.production {
background: $production-status-color;
}
.open {
background: $open-status-color;
}
.done {
background: $done-status-color;
}
.in-progress {
background: $in-progress-status-color;
}
.inactive {
background: $inactive-status-color;
}
.step-comment {
margin-top: 1rem;
font-size: $font-size-small;
}
.new-comment-button {
background-color: $whiteColor;
color: $darkGreyColor;
font-size: $font-size-small;
cursor: pointer;
margin-top: 0.5rem;
}
.support-mail-button {
background-color: $whiteColor;
color: $darkGreyColor;
font-size: $font-size-small;
cursor: pointer;
margin-top: 0.5rem;
float: right;
}
.upload-file-drop-zone {
width: 100%;
border: 3px dashed $borderColor;
height: 10rem;
cursor: pointer;
border-radius: 4px;
}
.upload-file-accept {
border: 3px dashed $greenColor;
background: rgba(52, 195, 136, 0.1);
}
.upload-file-drop-zone:hover{
border: 3px dashed $greenColor;
background: rgba(52, 195, 136, 0.1);
}
.status-icon {
width: $font-size-xsmal;
height: $font-size-xsmal;
display: inline-block;
border-radius: 50%;
margin-right: 0.5rem;
}
.drop-zone-text {
text-align: center;
padding: 4rem 0;
}
.document-link {
cursor: pointer;
word-wrap: break-word;
.step-comment {
padding: 0.5rem 1rem 1rem;
font-size: $font-size-small;
}
}
.order-package-info {
background: $whiteColor;
padding-bottom: 1rem;
.document-link {
cursor: pointer;
word-wrap: break-word;
font-size: $font-size-small;
}
.price-info-btn {
margin-left: 1rem;
cursor: pointer;
}
.price-info-popover {
max-width: 50rem;
}
.link-to-docs{
cursor: pointer;
color: $blueColor;
font-weight: 600;
}
}
#order-info-description {
background: $whiteColor;
padding-bottom: 1rem;
.terms-link {
cursor: pointer;
margin-top: 1rem;
}
.terms-label {
display: inline-block;
}
.installation-date-per-package {
font-weight: $font-weight;
}
}
#order-comments {
background: $whiteColor;
.order-coments-body{
margin: 1rem 0;
}
.order-comment {
margin-top: 0.5rem;
font-size: $font-size-small;
background: rgba(43, 98, 121, 0.2);
padding: 0.5rem;
border-radius: $box-radius;
}
.mine {
background: rgba(113, 194, 191, 0.2);
}
.order-comment-header {
font-weight: $font-weight;
}
}
.confirmation-proposed {
color: $pending-status-color;
}
.confirmation-accepted {
color: $done-status-color;
}
.confirmation-declined {
color: $canceled-status-color;
}
.confirmation-canceled {
color: $invalid-status-color;
}
.confirmation-invalid {
color: $invalid-status-color;
}
#order-documents{
.document-link-big {
display: inline-block;
cursor: pointer;
padding: 1rem;
margin-bottom: 3px;
text-align: center;
font-size: $font-size-small;
}
.document-link-big a {
color: $blueColor;
}
.document-link-big:hover {
margin-bottom: 0;
color: $title-color;
border-bottom: $blueColor 3px solid;
}
}
#send-support-mail-container {
.support-details {
margin-top: 1rem;
}
.package-name {
font-weight: $font-weight;
}
.order-package-details {
margin-bottom: 1.5rem;
}
}
#my-support-mail-text {
width: 50rem;
height: 10rem;
}
@media all and (max-width: 1032px) {
#my-support-mail-text {
width: 22rem;
}
}
@media all and (max-width: 768px) {
.drop-zone-text {
text-align: center;
padding: 4rem 0;
font-size: 1rem;
}
.document-link {
font-size: $font-size-small;
}
#my-support-mail-text {
width: 16rem;
}
}

View File

@@ -0,0 +1,70 @@
@import '../../../styleConstants.scss';
.process-nav {
.process-menu {
text-align: center;
}
.process-nav-div {
display: inline-block;
margin-left: 1rem;
cursor: pointer;
font-weight: $font-weight;
color: $blueColor;
}
.selected-nav {
border-bottom: $blueColor 3px solid;
color: $title-color;
}
.process-nav-div:hover {
border-bottom: $blueColor 3px solid;
}
.status-layer{
display: inline-block;
vertical-align: middle;
border-radius: 3px;
color: $whiteColor;
padding: 0.2rem 1rem;
font-size: $font-size-xsmal;
margin-left: 0.7rem;
font-weight: $font-weight;
}
.schedule-active {
cursor: pointer;
background-color: $whiteColor;
color: $darkGreyColor;
opacity: 0.8;
border: 0.1rem $border-grey solid;
display: inline-block;
border-radius: $box-radius;
font-weight: $font-weight;
}
.schedule-inactive {
cursor: no-drop;
background-color: $whiteColor;
color: $darkGreyColor;
opacity: 0.4;
border: 0.1rem $border-grey solid;
display: inline-block;
border-radius: $box-radius;
font-weight: $font-weight;
}
@media all and (max-width: 576px) {
.process-menu {
text-align: left;
}
.process-nav-div {
margin-left: 0;
margin-right: 1rem;
}
}
}

View File

@@ -0,0 +1,25 @@
@import '../../../styleConstants.scss';
#validate-questionnaire {
padding: 1rem 0;
.invalid {
background: $invalid-status-color;
}
.not-validated {
background: $pending-status-color;
}
.validated {
background: $validated-status-color;
}
.package-document {
margin-top: 1rem;
}
.document-status {
font-weight: $font-weight;
}
}

View File

@@ -0,0 +1,151 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Button} from 'reactstrap';
import BillingAddress from './components/BillingAddress.jsx';
import SelectBillingAddress from '../cart/components/SelectBillingAddress.jsx';
import AddEditBillingAddress from './components/AddEditBillingAddress.jsx';
import RemoveBillingAddress from './components/RemoveBillingAddress.jsx';
import {profileTexts} from '../../constants/profileSettingsConstants';
import {setDialogContent, setDialogOpenFlag} from '../../actions/dialog/dialogActions';
import {saveBillingAddress, removeBillingAddress} from '../../actions/profileSettings/addressActions';
import './style/AddressesContainer.css';
class BillingAddressesContainer extends Component {
constructor(props) {
super(props);
this.changedAddress = {};
this.openAddressDialog = this.openAddressDialog.bind(this);
this.onAddressChange = this.onAddressChange.bind(this);
}
saveBillingAddress(address){
this.props.dispatch(saveBillingAddress(this.props.idUser, this.props.idCompany, address));
}
onAddressChange(address){
this.changedAddress = Object.assign({}, address);
}
removeBillingAddress(address){
this.props.dispatch(removeBillingAddress(this.props.idUser, address.id));
}
getDialogContent(action, address){
if(action === 'add'){
return {
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.SAVE ,
id: 'save-profile-address-btn',
action: () => {this.saveBillingAddress(this.changedAddress)},
waitForAction: true
},
{
color: 'secondary',
name: profileTexts.buttons.CANCEL,
id: 'cancel-profile-address-btn'
}
],
header: profileTexts.labels.ADD_BILLING_ADDRESS,
TagName: AddEditBillingAddress,
class: 'address-dialog',
params: {address: {}, countries: this.props.countries, onAddressChange: this.onAddressChange}
};
}else if(action === 'edit'){
return {
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.SAVE ,
id: 'save-profile-address-btn',
action: () => {this.saveBillingAddress(this.changedAddress)},
waitForAction: true
},
{
color: 'secondary',
name: profileTexts.buttons.CANCEL,
id: 'cancel-profile-address-btn'
}
],
header: profileTexts.labels.EDIT_BILLING_ADDRESS,
TagName: AddEditBillingAddress,
class: 'address-dialog',
params: {address, countries: this.props.countries, onAddressChange: this.onAddressChange}
};
}else if(action === 'remove'){
return{
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.YES ,
id: 'remove-profile-address-btn',
action: () => {this.removeBillingAddress(address)}
},
{
color: 'secondary',
name: profileTexts.buttons.NO,
id: 'cancel-remove-profile-address-btn'
}
],
header: profileTexts.labels.REMOVE_BILLING_ADDRESS,
TagName: RemoveBillingAddress,
params: {address}
};
}else{
return {};
}
}
openAddressDialog(action, address){
const dialogContent = this.getDialogContent(action, address);
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
render() {
const {profileInfo} = this.props;
const {location, handleBillingChange, idSelectedBillingAddress} = this.props.params || '';
const TagName = location === 'cart' ? SelectBillingAddress : BillingAddress;
return (
<div>
<div id="billing-addresses" className="user-addresses edit-content">
<Row>
<Col>
<h5>{profileTexts.labels.BILLING_ADDRESSES}</h5>
<Button color="secondary"
onClick={() => {this.openAddressDialog('add')}}
className="add-address-btn wiaas-button">
<i className="fa fa-plus" aria-hidden="true"></i> {' '} {profileTexts.buttons.ADD_ADDRESS}
</Button>
</Col>
</Row>
<Row>
<Col>
{
(profileInfo && profileInfo.billingAddresses && profileInfo.billingAddresses.length > 0 ) &&
profileInfo.billingAddresses.map(address =>
<TagName key={'address-'+ address.id}
idSelectedBillingAddress={idSelectedBillingAddress}
handleBillingChange={handleBillingChange}
openAddressDialog={this.openAddressDialog}
billingAddress={address}/>)
}
</Col>
</Row>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({
profileInfo: state.profileSettingsReducer.profileInfo,
countries: state.profileSettingsReducer.countries
});
export default connect(mapStateToProps)(BillingAddressesContainer);

View File

@@ -0,0 +1,62 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Button, Alert} from 'reactstrap';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import {generatePassword} from '../../actions/login/authActions';
import {profileTexts} from '../../constants/profileSettingsConstants';
import {loginMessages} from '../../constants/authConstants';
class ChangePasswordContainer extends Component {
constructor(props){
super(props);
this.state = {
isPasswordChanged: false
};
this.generatePassword = this.generatePassword.bind(this);
}
generatePassword() {
const {mail} = this.props.profileInfo || '';
this.props.dispatch(generatePassword(mail));
this.setState({isPasswordChanged: true});
}
render() {
return (
<div>
<WiaasBox id="profile-edit">
<div className="edit-content">
<Row>
<Col>
<h5>{profileTexts.labels.CHANGE_PASSWORD}</h5>
</Col>
</Row>
<Row>
<Col>
<div>
<Button color="secondary"
onClick={this.generatePassword}
className="change-password wiaas-button">{profileTexts.buttons.GENERATE_TOKEN}</Button>
</div>
{
this.state.isPasswordChanged &&
<div id="change-password-message">
{loginMessages[this.props.errorMessage] && <Alert className='wiaas-alert' color={this.props.messageColor}>{loginMessages[this.props.errorMessage]}</Alert>}
</div>
}
</Col>
</Row>
</div>
</WiaasBox>
</div>
);
}
}
const mapStateToProps = (state) => ({
errorMessage: state.auth.errorMessage,
messageColor: state.auth.messageColor
});
export default connect(mapStateToProps)(ChangePasswordContainer);

View File

@@ -0,0 +1,80 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Form, FormGroup, Label, Input, Button} from 'reactstrap';
import {saveCompanyInfo} from '../../actions/profileSettings/profileSettingsActions';
import {profileTexts} from '../../constants/profileSettingsConstants';
class CompanyEditContainer extends Component {
constructor(props){
super(props);
const profile = this.props.profileInfo;
this.state = {
idCompany: profile.idCompany,
vatCode: profile.vatCode,
companyName: profile.companyName
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const field = event.target.name;
const profile = {};
profile[field] = event.target.value;
return this.setState(profile);
}
handleSubmit(event) {
this.props.dispatch(saveCompanyInfo(this.props.idUser, this.state));
event.preventDefault();
}
render() {
const {companyName, vatCode} = this.state;
return (
<div>
<div id="company-edit" className="edit-content">
<Row>
<Col>
<h5>{profileTexts.labels.COMPANY_EDIT_TITLE}</h5>
</Col>
</Row>
<Row>
<Col>
<Form onSubmit={this.handleSubmit} className="company-edit-content">
<FormGroup row>
<Label xl="2" for="company-name">{profileTexts.labels.COMPANY_NAME}</Label>
<Col xl="4">
<Input value={companyName}
onChange={this.handleChange}
type="text"
name="companyName"
id="company-name"
placeholder={profileTexts.labels.COMPANY_NAME} />
</Col>
</FormGroup>
<FormGroup row>
<Label xl="2" for="company-vat">{profileTexts.labels.VAT_CODE}</Label>
<Col xl="4" lg="6" md="6">
<Input value={vatCode}
onChange={this.handleChange}
type="text"
name="vatCode"
id="company-vat"
placeholder={profileTexts.labels.VAT_CODE} />
</Col>
</FormGroup>
<Button color="secondary" className="save-company-btn wiaas-button">{profileTexts.buttons.SAVE}</Button>
</Form>
</Col>
</Row>
</div>
</div>
);
}
}
export default connect()(CompanyEditContainer);

View File

@@ -0,0 +1,152 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Button} from 'reactstrap';
import ProfileAddress from './components/ProfileAddress.jsx';
import SelectDeliveryAddress from '../cart/components/SelectDeliveryAddress.jsx';
import AddEditProfileAddress from './components/AddEditProfileAddress.jsx';
import RemoveProfileAddress from './components/RemoveProfileAddress.jsx';
import {profileTexts} from '../../constants/profileSettingsConstants';
import {setDialogContent, setDialogOpenFlag} from '../../actions/dialog/dialogActions';
import {saveProfileAddress, removeProfileAddress} from '../../actions/profileSettings/addressActions';
import './style/AddressesContainer.css';
class ProfileAddressesContainer extends Component {
constructor(props) {
super(props);
this.changedAddress = {};
this.openAddressDialog = this.openAddressDialog.bind(this);
this.onAddressChange = this.onAddressChange.bind(this);
}
saveProfileAddress(profileAddress){
this.props.dispatch(saveProfileAddress(this.props.idUser, profileAddress));
}
onAddressChange(address){
this.changedAddress = Object.assign({}, address);
}
removeProfileAddress(profileAddress){
this.props.dispatch(removeProfileAddress(this.props.idUser, profileAddress.id));
}
getDialogContent(action, profileAddress){
if(action === 'add'){
return {
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.SAVE ,
id: 'save-profile-address-btn',
action: () => {this.saveProfileAddress(this.changedAddress)},
waitForAction: true
},
{
color: 'secondary',
name: profileTexts.buttons.CANCEL,
id: 'cancel-profile-address-btn'
}
],
header: profileTexts.labels.ADD_PROFILE_ADDRESS,
TagName: AddEditProfileAddress,
class: 'address-dialog',
params: {profileAddress: {}, countries: this.props.countries, onAddressChange: this.onAddressChange}
};
}else if(action === 'edit'){
return {
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.SAVE ,
id: 'save-profile-address-btn',
action: () => {this.saveProfileAddress(this.changedAddress)},
waitForAction: true
},
{
color: 'secondary',
name: profileTexts.buttons.CANCEL,
id: 'cancel-profile-address-btn'
}
],
header: profileTexts.labels.EDIT_PROFILE_ADDRESS,
TagName: AddEditProfileAddress,
class: 'address-dialog',
params: {profileAddress, countries: this.props.countries, onAddressChange: this.onAddressChange}
};
}else if(action === 'remove'){
return{
buttons: [
{
color: 'secondary',
name: profileTexts.buttons.YES ,
id: 'remove-profile-address-btn',
action: () => {this.removeProfileAddress(profileAddress)}
},
{
color: 'secondary',
name: profileTexts.buttons.NO,
id: 'cancel-remove-profile-address-btn'
}
],
header: profileTexts.labels.REMOVE_PROFILE_ADDRESS,
TagName: RemoveProfileAddress,
params: {profileAddress}
};
}else{
return {};
}
}
openAddressDialog(action, profileAddress) {
const dialogContent = this.getDialogContent(action, profileAddress);
this.props.dispatch(setDialogOpenFlag(true));
this.props.dispatch(setDialogContent(dialogContent));
}
render() {
const {profileInfo} = this.props;
const { location, handleDeliveryChange, idSelectedDeliveryAddress} = this.props.params || '';
const TagName = location === 'cart' ? SelectDeliveryAddress : ProfileAddress;
return (
<div>
<div id="profile-addresses" className="user-addresses edit-content">
<Row>
<Col>
<h5>{profileTexts.labels.PROFILE_ADDRESSES}</h5>
<Button color="secondary"
onClick={() => {this.openAddressDialog('add')}}
className="add-address-btn wiaas-button">
<i className="fa fa-plus" aria-hidden="true"></i> {' '} {profileTexts.buttons.ADD_ADDRESS}
</Button>
</Col>
</Row>
<Row>
<Col>
<div>
{
(profileInfo && profileInfo.profileAddresses && profileInfo.profileAddresses.length > 0 ) &&
profileInfo.profileAddresses.map(address =>
<TagName key={'address-'+ address.id}
idSelectedDeliveryAddress={idSelectedDeliveryAddress}
handleDeliveryChange={handleDeliveryChange}
openAddressDialog={this.openAddressDialog}
profileAddress={address}/>)
}
</div>
</Col>
</Row>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({
profileInfo: state.profileSettingsReducer.profileInfo,
countries: state.profileSettingsReducer.countries
});
export default connect(mapStateToProps)(ProfileAddressesContainer);

View File

@@ -0,0 +1,81 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Row, Col, Form, FormGroup, Label, Input, Button} from 'reactstrap';
import {saveProfileInfo} from '../../actions/profileSettings/profileSettingsActions';
import {profileTexts} from '../../constants/profileSettingsConstants';
class ProfileEditContainer extends Component {
constructor(props){
super(props);
const profile = this.props.profileInfo;
this.state = {
name: profile.name,
phone: profile.phone,
idUserType: profile.idUserType,
id: profile.id
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const field = event.target.name;
const profile = {};
profile[field] = event.target.value;
return this.setState(profile);
}
handleSubmit(event) {
this.props.dispatch(saveProfileInfo(this.props.idUser, this.state));
event.preventDefault();
}
render() {
const {name, phone} = this.state;
return (
<div>
<div id="profile-edit" className="edit-content">
<Row>
<Col>
<h5>{profileTexts.labels.PROFILE_EDIT_TITLE}</h5>
</Col>
</Row>
<Row>
<Col>
<Form onSubmit={this.handleSubmit} className="profile-edit-content">
<FormGroup row>
<Label xl="2" for="profile-name">{profileTexts.labels.PROFILE_NAME}</Label>
<Col xl="4">
<Input value={name}
onChange={this.handleChange}
type="text"
name="name"
id="profile-name"
placeholder={profileTexts.labels.PROFILE_NAME} />
</Col>
</FormGroup>
<FormGroup row>
<Label xl="2" for="profile-phone">{profileTexts.labels.PROFILE_PHONE}</Label>
<Col xl="4">
<Input value={phone}
onChange={this.handleChange}
type="text"
name="phone"
id="profile-phone"
placeholder={profileTexts.labels.PROFILE_PHONE} />
</Col>
</FormGroup>
<Button color="secondary" className="save-profile-btn wiaas-button">{profileTexts.buttons.SAVE}</Button>
</Form>
</Col>
</Row>
</div>
</div>
);
}
}
export default connect()(ProfileEditContainer);

View File

@@ -0,0 +1,60 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Container, Row, Col} from 'reactstrap';
import ProfileEditContainer from './ProfileEditContainer.jsx';
import CompanyEditContainer from './CompanyEditContainer.jsx';
import ProfileShowContainer from './ProfileShowContainer.jsx';
import ProfileAddressesContainer from './ProfileAddressesContainer.jsx';
import BillingAddressesContainer from './BillingAddressesContainer.jsx';
import ChangePasswordContainer from './ChangePasswordContainer.jsx';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import {fetchProfileInfo, fetchCountries} from '../../actions/profileSettings/profileSettingsActions';
import './style/ProfieSettingsContainer.css';
class ProfileSettingsContainer extends Component {
componentDidMount(){
this.props.dispatch(fetchProfileInfo(this.props.userInfo.wiaas_id_user));
this.props.dispatch(fetchCountries());
}
render() {
const {profileInfo, isLoading} = this.props;
return (
<Container fluid={true} id="proffile-settings">
{
isLoading &&
<div className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</div>
}
{
(profileInfo && !isLoading) &&
<Row>
<Col xl="8" lg="8" md="6" xs="12">
<WiaasBox>
<ProfileEditContainer idUser={this.props.userInfo.wiaas_id_user} profileInfo={this.props.profileInfo}/>
{
profileInfo.isCompanyAdmin === 1 &&
<CompanyEditContainer idUser={this.props.userInfo.wiaas_id_user} profileInfo={profileInfo}/>
}
<ProfileAddressesContainer idUser={this.props.userInfo.wiaas_id_user}/>
<BillingAddressesContainer idCompany={profileInfo.idCompany} idUser={this.props.userInfo.wiaas_id_user}/>
</WiaasBox>
</Col>
<Col xl="4" lg="4" md="6" xs="12">
<ProfileShowContainer profileInfo={this.props.profileInfo}/>
<ChangePasswordContainer profileInfo={this.props.profileInfo}/>
</Col>
</Row>
}
</Container>
);
}
}
const mapStateToProps = (state) => ({
profileInfo: state.profileSettingsReducer.profileInfo,
isLoading: state.profileSettingsReducer.isLoading,
userInfo: state.auth.userInfo
});
export default connect(mapStateToProps)(ProfileSettingsContainer);

View File

@@ -0,0 +1,52 @@
import React, {Component} from 'react';
import {Col, Card, CardImg, CardBody, CardTitle} from 'reactstrap';
import WiaasBox from '../../mainComponents/box/WiaasBox.jsx';
import profileSvg from '../../svg/profile.svg';
import './style/ProfileShowContainer.css';
class ProfileShowContainer extends Component {
render() {
const {profileInfo, isLoading} = this.props;
return (
<div>
<WiaasBox id="profile-show">
{
isLoading &&
<Col xl="12" className="loader">
<i className="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i>
</Col>
}
{
(profileInfo && !isLoading) &&
<Card id='profile-info-card'>
<CardImg top
className="profile-image"
src={profileSvg}
alt="My Profile" />
<CardBody className="text-center">
<CardTitle>{profileInfo.name}</CardTitle>
<div>
<div>
{profileInfo.companyName}
</div>
<div>
{profileInfo.vatCode}
</div>
<div>
<i className="fa fa-envelope" aria-hidden="true"></i> {profileInfo.mail}
</div>
<div>
<i className="fa fa-phone" aria-hidden="true"></i> {profileInfo.phone}
</div>
</div>
</CardBody>
</Card>
}
</WiaasBox>
</div>
);
}
}
export default ProfileShowContainer;

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