initial docker setup
This commit is contained in:
47
frontend/src/containers/ContentContainer.jsx
Normal file
47
frontend/src/containers/ContentContainer.jsx
Normal 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);
|
||||
44
frontend/src/containers/cart/CartContainer.jsx
Normal file
44
frontend/src/containers/cart/CartContainer.jsx
Normal 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);
|
||||
229
frontend/src/containers/cart/CartCustomerDetailsContainer.jsx
Normal file
229
frontend/src/containers/cart/CartCustomerDetailsContainer.jsx
Normal 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);
|
||||
83
frontend/src/containers/cart/CartItemsContainer.jsx
Normal file
83
frontend/src/containers/cart/CartItemsContainer.jsx
Normal 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);
|
||||
177
frontend/src/containers/cart/CartReviewOrderContainer.jsx
Normal file
177
frontend/src/containers/cart/CartReviewOrderContainer.jsx
Normal 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);
|
||||
75
frontend/src/containers/cart/CartStepsContainer.jsx
Normal file
75
frontend/src/containers/cart/CartStepsContainer.jsx
Normal 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);
|
||||
@@ -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);
|
||||
49
frontend/src/containers/cart/components/AddOrderProject.jsx
Normal file
49
frontend/src/containers/cart/components/AddOrderProject.jsx
Normal 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);
|
||||
40
frontend/src/containers/cart/components/BidItem.jsx
Normal file
40
frontend/src/containers/cart/components/BidItem.jsx
Normal 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);
|
||||
34
frontend/src/containers/cart/components/BidsList.jsx
Normal file
34
frontend/src/containers/cart/components/BidsList.jsx
Normal 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;
|
||||
23
frontend/src/containers/cart/components/CartIcon.jsx
Normal file
23
frontend/src/containers/cart/components/CartIcon.jsx
Normal 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;
|
||||
272
frontend/src/containers/cart/components/CartItem.jsx
Normal file
272
frontend/src/containers/cart/components/CartItem.jsx
Normal 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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
47
frontend/src/containers/cart/components/PackageBids.jsx
Normal file
47
frontend/src/containers/cart/components/PackageBids.jsx
Normal 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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
170
frontend/src/containers/cart/style/Cart.scss
Normal file
170
frontend/src/containers/cart/style/Cart.scss
Normal 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;
|
||||
}
|
||||
165
frontend/src/containers/cart/style/CartStepsContainer.scss
Normal file
165
frontend/src/containers/cart/style/CartStepsContainer.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
frontend/src/containers/cart/style/OrderProjects.scss
Normal file
20
frontend/src/containers/cart/style/OrderProjects.scss
Normal 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);
|
||||
}
|
||||
}
|
||||
13
frontend/src/containers/cart/style/PacakgeBids.scss
Normal file
13
frontend/src/containers/cart/style/PacakgeBids.scss
Normal 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%;
|
||||
}
|
||||
27
frontend/src/containers/coMarket/CoMarketContainer.jsx
Normal file
27
frontend/src/containers/coMarket/CoMarketContainer.jsx
Normal 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);
|
||||
45
frontend/src/containers/coMarket/CoMarketNavContainer.jsx
Normal file
45
frontend/src/containers/coMarket/CoMarketNavContainer.jsx
Normal 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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
20
frontend/src/containers/coMarket/components/CartIcon.jsx
Normal file
20
frontend/src/containers/coMarket/components/CartIcon.jsx
Normal 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;
|
||||
@@ -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);
|
||||
61
frontend/src/containers/coMarket/components/PackageInfo.jsx
Normal file
61
frontend/src/containers/coMarket/components/PackageInfo.jsx
Normal 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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
80
frontend/src/containers/coMarket/components/PackagePrice.jsx
Normal file
80
frontend/src/containers/coMarket/components/PackagePrice.jsx
Normal 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);
|
||||
45
frontend/src/containers/coMarket/components/ShopItem.jsx
Normal file
45
frontend/src/containers/coMarket/components/ShopItem.jsx
Normal 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;
|
||||
175
frontend/src/containers/coMarket/style/CoMarket.scss
Normal file
175
frontend/src/containers/coMarket/style/CoMarket.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
42
frontend/src/containers/contentContainer.scss
Normal file
42
frontend/src/containers/contentContainer.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
19
frontend/src/containers/dashboard/Dashboards.scss
Normal file
19
frontend/src/containers/dashboard/Dashboards.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
83
frontend/src/containers/dashboard/DashboardsContainer.jsx
Normal file
83
frontend/src/containers/dashboard/DashboardsContainer.jsx
Normal 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);
|
||||
46
frontend/src/containers/dashboard/NextActions.scss
Normal file
46
frontend/src/containers/dashboard/NextActions.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
41
frontend/src/containers/dashboard/NextActionsContainer.jsx
Normal file
41
frontend/src/containers/dashboard/NextActionsContainer.jsx
Normal 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);
|
||||
45
frontend/src/containers/dashboard/OrderCentral.scss
Normal file
45
frontend/src/containers/dashboard/OrderCentral.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
69
frontend/src/containers/dashboard/OrderCentralContainer.jsx
Normal file
69
frontend/src/containers/dashboard/OrderCentralContainer.jsx
Normal 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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
34
frontend/src/containers/dashboard/components/OrderItem.jsx
Normal file
34
frontend/src/containers/dashboard/components/OrderItem.jsx
Normal 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;
|
||||
59
frontend/src/containers/dashboard/components/OrdersList.jsx
Normal file
59
frontend/src/containers/dashboard/components/OrdersList.jsx
Normal 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;
|
||||
17
frontend/src/containers/footer/Footer.jsx
Normal file
17
frontend/src/containers/footer/Footer.jsx
Normal 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;
|
||||
12
frontend/src/containers/footer/style/Footer.scss
Normal file
12
frontend/src/containers/footer/style/Footer.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
@import '../../../styleConstants.scss';
|
||||
|
||||
footer {
|
||||
.footer-btn {
|
||||
cursor: pointer;
|
||||
color: $whiteColor;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog{
|
||||
max-width: 80%;
|
||||
}
|
||||
90
frontend/src/containers/login/ChangePasswordContainer.jsx
Normal file
90
frontend/src/containers/login/ChangePasswordContainer.jsx
Normal 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);
|
||||
91
frontend/src/containers/login/LogInContainer.jsx
Normal file
91
frontend/src/containers/login/LogInContainer.jsx
Normal 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;
|
||||
128
frontend/src/containers/login/LogInForm.jsx
Normal file
128
frontend/src/containers/login/LogInForm.jsx
Normal 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);
|
||||
208
frontend/src/containers/login/login.scss
Normal file
208
frontend/src/containers/login/login.scss
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
78
frontend/src/containers/orders/OrdersContainer.jsx
Normal file
78
frontend/src/containers/orders/OrdersContainer.jsx
Normal 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;
|
||||
57
frontend/src/containers/orders/OrdersDataContainer.jsx
Normal file
57
frontend/src/containers/orders/OrdersDataContainer.jsx
Normal 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);
|
||||
267
frontend/src/containers/orders/ProcessContainer.jsx
Normal file
267
frontend/src/containers/orders/ProcessContainer.jsx
Normal 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);
|
||||
110
frontend/src/containers/orders/ProcessNavContainer.jsx
Normal file
110
frontend/src/containers/orders/ProcessNavContainer.jsx
Normal 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);
|
||||
@@ -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);
|
||||
26
frontend/src/containers/orders/components/AddComment.jsx
Normal file
26
frontend/src/containers/orders/components/AddComment.jsx
Normal 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;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
72
frontend/src/containers/orders/components/OrderComments.jsx
Normal file
72
frontend/src/containers/orders/components/OrderComments.jsx
Normal 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);
|
||||
26
frontend/src/containers/orders/components/OrderDocuments.jsx
Normal file
26
frontend/src/containers/orders/components/OrderDocuments.jsx
Normal 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);
|
||||
@@ -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;
|
||||
152
frontend/src/containers/orders/components/OrderInfo.jsx
Normal file
152
frontend/src/containers/orders/components/OrderInfo.jsx
Normal 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);
|
||||
75
frontend/src/containers/orders/components/OrderList.jsx
Normal file
75
frontend/src/containers/orders/components/OrderList.jsx
Normal 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);
|
||||
@@ -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);
|
||||
92
frontend/src/containers/orders/components/OrderPackage.jsx
Normal file
92
frontend/src/containers/orders/components/OrderPackage.jsx
Normal 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;
|
||||
35
frontend/src/containers/orders/components/PackagesNav.jsx
Normal file
35
frontend/src/containers/orders/components/PackagesNav.jsx
Normal 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;
|
||||
121
frontend/src/containers/orders/components/SupportMail.jsx
Normal file
121
frontend/src/containers/orders/components/SupportMail.jsx
Normal 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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
66
frontend/src/containers/orders/style/CustomerAcceptance.scss
Normal file
66
frontend/src/containers/orders/style/CustomerAcceptance.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
frontend/src/containers/orders/style/InstallationScheduling.scss
Normal file
112
frontend/src/containers/orders/style/InstallationScheduling.scss
Normal 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;
|
||||
}
|
||||
128
frontend/src/containers/orders/style/Orders.scss
Normal file
128
frontend/src/containers/orders/style/Orders.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
3
frontend/src/containers/orders/style/OrdersList.scss
Normal file
3
frontend/src/containers/orders/style/OrdersList.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.view-all-orders-text {
|
||||
padding-left: 0.3rem;
|
||||
}
|
||||
366
frontend/src/containers/orders/style/ProcessContainer.scss
Normal file
366
frontend/src/containers/orders/style/ProcessContainer.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
Reference in New Issue
Block a user