Add package addons and options

This commit is contained in:
Almira Krdzic
2018-09-06 23:29:29 +02:00
parent 5bef0b9e5e
commit cd5f09487d
25 changed files with 1302 additions and 482 deletions

View File

@@ -71,16 +71,18 @@ export const fetchPackageDetails = (params) => {
}
}
const generateOptions = (selectedOptions, selectedAdditionals, selectedAgreement) => {
const extraPackages = [];
const collectPackageOptionsAndAdditionals = (selectedOptions, selectedAdditionals, selectedAgreement) => {
const optionPackages = [];
const additionalPackages = [];
const unavailablePackages = [];
if(selectedAdditionals && selectedAdditionals.length){
selectedAdditionals.forEach(additional => {
const selectedPrice = priceHelper.getSelectedPrice(additional, selectedAgreement);
if(!selectedPrice){
unavailablePackages.push(additional);
}else{
extraPackages.push(additional.idAdditionalPackage);
additionalPackages.push(additional.idAdditionalPackage);
}
});
}
@@ -91,12 +93,12 @@ const generateOptions = (selectedOptions, selectedAdditionals, selectedAgreement
if(!selectedPrice){
unavailablePackages.push(selectedOptions[idGroup]);
}else{
extraPackages.push(selectedOptions[idGroup].idOptionPackage);
optionPackages.push(selectedOptions[idGroup].idOptionPackage);
}
});
}
return {extraPackages, unavailablePackages};
return {optionPackages, additionalPackages, unavailablePackages};
};
const requestAddToCart = () => ({
@@ -104,20 +106,24 @@ const requestAddToCart = () => ({
});
export const addToCart = (addParams) => {
// const options = generateOptions(addParams.selectedOptions, addParams.selectedAdditionals, addParams.selectedAgreement);
const result = collectPackageOptionsAndAdditionals(
addParams.selectedOptions,
addParams.selectedAdditionals,
addParams.selectedAgreement);
const params = {
'package_id': addParams.selectedPackage.id,
'price_id': addParams.selectedAgreement.idPrice,
//options: options.extraPackages
'addons': result.additionalPackages,
'options': result.optionPackages
};
// if(options.unavailablePackages.length){
// const unavailable = options.unavailablePackages.map((unavailable) =>{return unavailable.optionName || unavailable.packageName;});
// const message = coMarketMessages.UNAVAILABLE_PACKAGES + ' ' + unavailable.join();
//
// return dispatch => {dispatch(updateMessages([{code: 'warning', message}], coMarketMessages))};
// }
if(result.unavailablePackages.length){
const unavailable = result.unavailablePackages.map((unavailable) =>{return unavailable.optionName || unavailable.packageName;});
const message = coMarketMessages.UNAVAILABLE_PACKAGES + ' ' + unavailable.join();
return dispatch => {dispatch(updateMessages([{code: 'warning', message}], coMarketMessages))};
}
return dispatch => {
dispatch(requestAddToCart());

View File

@@ -2,7 +2,7 @@
#cart-count {
vertical-align: middle;
color: $cart-count-color;
color: $accentColor;
font-size: 1.5rem;
border-radius: 1rem;
font-family: arial,sans-serif;

View File

@@ -5,7 +5,7 @@ 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 PackageOptionGroup from './components/PackageOptionGroup';
import AdditionalPackages from './components/AdditionalPackages.jsx';
import AgreementOptions from './components/AgreementOptions.jsx';
import {coMarketTexts} from '../../constants/coMarketConstants';
@@ -64,21 +64,30 @@ class CoMarketPackageDetailsContainer extends Component {
<Col xl="6" lg="7" md="12" xs="12">
<div id="shop-package-buy-info">
{
selectedPackage.groups && Object.keys(selectedPackage.groups).length > 0 && selectedAgreement &&
<PackageOptions
groups={selectedPackage.groups}
country={selectedPackage.country}/>
}
<PackagePrice currency={selectedPackage.currency} />
<AgreementOptions
prices={selectedPackage.prices}
currency={selectedPackage.currency}/>
{
selectedPackage.additionalPackages && selectedPackage.additionalPackages.length > 0 && selectedAgreement &&
<AdditionalPackages
additionalPackages={selectedPackage.additionalPackages}
country={selectedPackage.country}/>
currency={selectedPackage.currency}/>
}
{
selectedPackage.groups && Object.keys(selectedPackage.groups).length > 0 && selectedAgreement &&
(<div className="shop-package-options">
{
Object.keys(selectedPackage.groups).map(groupKey => {
const group = selectedPackage.groups[groupKey];
return (<PackageOptionGroup
key={groupKey}
group={group}
currency={selectedPackage.currency}/>);
})
}
</div>)
}
<AgreementOptions
prices={selectedPackage.prices}
currency={selectedPackage.currency}/>
<Row className="justify-content-end" style={{ marginTop: 30 }}>
<Button id="add-to-cart-btn"
className="add-to-cart-btn"

View File

@@ -1,6 +1,6 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col} from 'reactstrap';
import {Label, Popover, PopoverBody, Input, Col, Row} from 'reactstrap';
import {selectAdditional, removetAdditional} from '../../../actions/coMarket/coMarketPackageDetailsActions';
import PriceHelper from '../../../helpers/coMarket/PriceHelper';
import {coMarketTexts} from '../../../constants/coMarketConstants';
@@ -34,7 +34,7 @@ class AdditionalPackageItem extends Component {
}
isChecked() {
return this.props.selectedAdditionals
return !!this.props.selectedAdditionals
&& this.props.selectedAdditionals.includes(this.props.additionalPackage);
}
@@ -44,9 +44,9 @@ class AdditionalPackageItem extends Component {
});
}
getClass(isChecked) {
return isChecked ?
'selected-option' :
getClass() {
return this.isChecked() ?
'shop-package-option-selected' :
'';
}
@@ -55,62 +55,59 @@ class AdditionalPackageItem extends Component {
}
render() {
const {additionalPackage, country} = this.props;
const {additionalPackage, currency} = 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>
<div
id={'info-additonal-' + additionalPackage.idAdditionalPackage}
className={`shop-package-option d-flex ${this.getClass()}`}
>
<Label check className="d-flex flex-grow-1 flex-column">
<div className="d-flex">
<Col className="col-6 d-flex align-items-center">
<Input type="checkbox"
checked={isChecked}
onChange={this.handleOptionChange}
name={'package-option-'+ additionalPackage.idAdditionalPackage}
className="package-option-input"
/>
<span className="option-name">
{this.formatName(additionalPackage.packageName)}
</span>
</Col>
<Col className="col-3 d-flex align-items-center">
{
priceHelper.hasFixedPrice(selectedPrice) && (<span>
{selectedPrice.fixedExtra && selectedPrice.fixedExtra.toLocaleString() + ' ' + currency}
</span>)
}
</Col>
<Col className="col-3 d-flex align-items-center">
{
priceHelper.hasFixedPrice(selectedPrice) && priceHelper.hasRecurrentPrice(selectedPrice) ?
(<span>{priceHelper.sumPrices([selectedPrice.recurentExtra, selectedPrice.servicesExtra]).toLocaleString()} {currency}</span>) :
<span>0</span>
}
</Col>
</div>
<div
className="shop-package-option-description"
dangerouslySetInnerHTML={{__html: additionalPackage.shortDescription}}
></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>
}
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-additonal-' + additionalPackage.idAdditionalPackage}
@@ -119,7 +116,7 @@ class AdditionalPackageItem extends Component {
{coMarketTexts.labels.SELECTION_NOT_AVAILABLE}
</PopoverBody>
</Popover>
</Col>
</div>
);
}
}

View File

@@ -5,19 +5,19 @@ import {coMarketTexts} from '../../../constants/coMarketConstants';
class AdditionalPackages extends Component {
render() {
const {additionalPackages, country} = this.props;
const {additionalPackages, currency} = 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>
<h6 className="shop-package-option-header">
{coMarketTexts.labels.ADDITIONAL_PACKAGES}:
</h6>
{
additionalPackages.map(additionalPackage => {
return <AdditionalPackageItemContainer key={'additonal-' + additionalPackage.idAdditionalPackage}
currency={currency}
additionalPackage={additionalPackage}/>
})
}
</div>
);
}

View File

@@ -1,6 +1,6 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col, Row} from 'reactstrap';
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';
@@ -30,7 +30,7 @@ class AgreementOptionItem extends Component {
getClass(selectedAgreement, price) {
return selectedAgreement.idPaymentType === price.idPaymentType ?
'selected-option' :
'shop-package-option-selected' :
'';
}
@@ -38,11 +38,9 @@ class AgreementOptionItem extends Component {
const {price, selectedAgreement, currency} = this.props;
return (
<Row
className={`option-selection ${this.getClass(selectedAgreement, price)}`}
>
<Label check className="row flex-grow-1 no-wrap">
<Col className="d-flex align-items-center">
<div className={`shop-package-option d-flex ${this.getClass(selectedAgreement, price)}`}>
<Label check className="d-flex flex-grow-1 no-wrap">
<Col className="d-flex align-items-center col-6">
<Input type="radio"
name="price-type"
onChange={this.handleOptionChange}
@@ -51,10 +49,10 @@ class AgreementOptionItem extends Component {
className="price-type-option"/>
<span className="option-name">{price.payType}</span>
</Col>
<Col className="d-flex align-items-center">
<Col className="d-flex align-items-center col-3">
<span>{price.fixedExtra.toLocaleString()} {currency}</span>
</Col>
<Col className="d-flex align-items-center">
<Col className="d-flex align-items-center col-3">
<span className="option-value-text monthly-price">
{
priceHelper.hasRecurrentPrice(price)
@@ -64,10 +62,12 @@ class AgreementOptionItem extends Component {
</span>
</Col>
</Label>
<i className="price-info-btn fa fa-info-circle d-flex align-items-center"
aria-hidden="true"
id={'info-additonal-' + price.idPaymentType}
onClick={this.toggle}></i>
<i
className="price-info-btn fa fa-info-circle d-flex align-items-center"
aria-hidden="true"
id={'info-additonal-' + price.idPaymentType}
onClick={this.toggle}
></i>
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-additonal-' + price.idPaymentType}
@@ -105,7 +105,7 @@ class AgreementOptionItem extends Component {
</div>
</PopoverBody>
</Popover>
</Row>
</div>
);
}
}

View File

@@ -1,86 +1,19 @@
import React, {Component} from 'react';
import {Row, Col} from 'reactstrap';
import AgreementOptionItemContainer from './AgreementOptionItemContainer.jsx';
import {coMarketTexts} from '../../../constants/coMarketConstants';
import {connect} from "react-redux";
class AgreementOptions 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 {prices, currency, selectedAgreement, selectedOptions, selectedAdditionals} = this.props;
const {prices, currency} = this.props;
return (
<div className="shop-package-pricing">
<Row className="justify-content-end">
<Col className="col-4">
<h6 className="shop-package-label options-header">{coMarketTexts.labels.ON_DELIVERY}</h6>
</Col>
<Col className="col-4">
<h6 className="shop-package-label options-header">{coMarketTexts.labels.MONTHLY}</h6>
</Col>
</Row>
<div className="shop-package-options">
{
prices && prices.map((price) => {
return (<AgreementOptionItemContainer key={'agreement-' + price.idPaymentType} currency={currency} price={price}/>)
})
}
</div>
<Row className="justify-content-end shop-package-calculated-price">
<Col>
<h6>Price:</h6>
</Col>
<Col className="col-4">
{this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'fixedExtra').toLocaleString()} {currency}
</Col>
<Col className="col-4">
{(this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'recurentExtra')
+ this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'servicesExtra')).toLocaleString()}
{' '}{currency}
</Col>
</Row>
<div className="shop-package-options shop-package-options-pricing">
{
prices && prices.map((price) => {
return (<AgreementOptionItemContainer key={'agreement-' + price.idPaymentType} currency={currency} price={price}/>)
})
}
</div>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedOptions: state.coMarketPackageDetailsReducer.selectedOptions,
selectedAdditionals: state.coMarketPackageDetailsReducer.selectedAdditionals
});
export default connect(mapStateToProps) (AgreementOptions);
export default AgreementOptions;

View File

@@ -0,0 +1,80 @@
import React, {Component} from 'react';
import {Row, Col, Dropdown, DropdownToggle, DropdownMenu } from 'reactstrap';
import PackageOptionItemContainer from './PackageOptionItemContainer';
import {connect} from 'react-redux';
class PackageOptionGroup extends Component {
constructor(props) {
super(props);
this.toggleDropdown = this.toggleDropdown.bind(this);
this.state = {
dropdownOpen: false
};
}
toggleDropdown(groupKey) {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
getClass() {
if (this.props.selectedOptions && this.props.selectedOptions[this.props.group.idGroup]) {
return 'shop-package-option-selected';
}
return undefined;
}
render() {
const {group, currency, selectedOptions} = this.props;
const selectedGroupOption = selectedOptions ? selectedOptions[group.idGroup] : undefined;
return (
<div key={'group-' + group.idGroup} className={this.getClass()}>
<Dropdown
isOpen={this.state.dropdownOpen}
toggle={this.toggleDropdown}
className="w-100">
<DropdownToggle
caret
className="w-100 d-flex justify-content-between align-items-center"
tag="div"
>
<div className="d-flex flex-column flex-grow-1">
<small className="shop-package-option-header"> {group.groupName}:</small>
{
selectedGroupOption ?
(<PackageOptionItemContainer
option={selectedGroupOption}
currency={currency}
idGroup={group.idGroup}
simplified
/>) :
(<span>Select ...</span>)
}
</div>
</DropdownToggle>
<DropdownMenu className="w-100">
{
group.options.map(option => {
return (<PackageOptionItemContainer
key={option.idOptionPackage}
option={option}
currency={currency}
idGroup={group.idGroup}/>)
})
}
</DropdownMenu>
</Dropdown>
</div>
);
}
}
const mapStateToProps = (state) => ({
selectedAgreement: state.coMarketPackageDetailsReducer.selectedAgreement,
selectedOptions: state.coMarketPackageDetailsReducer.selectedOptions
});
export default connect(mapStateToProps) (PackageOptionGroup);

View File

@@ -1,6 +1,6 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Label, Popover, PopoverBody, Input, Col} from 'reactstrap';
import {Label, Popover, PopoverBody, Input, Row, Col} from 'reactstrap';
import {selectOption} from '../../../actions/coMarket/coMarketPackageDetailsActions';
import PriceHelper from '../../../helpers/coMarket/PriceHelper';
import {coMarketTexts} from '../../../constants/coMarketConstants';
@@ -30,14 +30,14 @@ class PackageOptionItem extends Component {
}
isChecked() {
return this.props.selectedOptions
&& this.props.selectedOptions[this.props.idGroup]
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' :
getClass() {
return this.isChecked() ?
'shop-package-option-selected' :
'';
}
@@ -46,63 +46,83 @@ class PackageOptionItem extends Component {
}
render() {
const {idGroup, option, country} = this.props;
const {idGroup, option, currency, simplified} = 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>
if (simplified) {
return (<div className="d-flex">
<Col className="d-flex align-items-center col-6">
<span className="option-name">
{this.formatName(option.optionName)}
</span>
</Col>
<Col className="d-flex align-items-center col-3">
{
!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>
priceHelper.hasFixedPrice(selectedPrice) && (<span>
{selectedPrice.fixedExtra && selectedPrice.fixedExtra.toLocaleString() + ' ' + currency}
</span>)
}
</Col>
<Col className="d-flex align-items-center col-3">
{
priceHelper.hasFixedPrice(selectedPrice) && priceHelper.hasRecurrentPrice(selectedPrice) ?
(<span>{priceHelper.sumPrices([selectedPrice.recurentExtra, selectedPrice.servicesExtra]).toLocaleString()} {currency}</span>) :
<span>0</span>
}
</Col>
</div>)
}
return (
<div
id={'info-option-' + idGroup + '-' + option.idOptionPackage}
className={`shop-package-option d-flex ${this.getClass()}`}
>
<Label check className="d-flex flex-grow-1 flex-column">
<div className="d-flex">
<Col className="col-6 d-flex align-items-center">
<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>
</Col>
<Col className="col-3 d-flex align-items-center">
{
priceHelper.hasFixedPrice(selectedPrice) && (<span>
{selectedPrice.fixedExtra && selectedPrice.fixedExtra.toLocaleString() + ' ' + currency}
</span>)
}
</Col>
<Col className="col-3 d-flex align-items-center">
{
priceHelper.hasFixedPrice(selectedPrice) && priceHelper.hasRecurrentPrice(selectedPrice) ?
(<span>{priceHelper.sumPrices([selectedPrice.recurentExtra, selectedPrice.servicesExtra]).toLocaleString()} {currency}</span>) :
<span>0</span>
}
</Col>
</div>
<div
className="shop-package-option-description"
dangerouslySetInnerHTML={{__html: option.shortDescription}}></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>
}
<Popover placement="bottom"
isOpen={this.state.popoverOpen}
target={'info-option-' + idGroup + '-' + option.idOptionPackage}
@@ -111,7 +131,7 @@ class PackageOptionItem extends Component {
{coMarketTexts.labels.SELECTION_NOT_AVAILABLE}
</PopoverBody>
</Popover>
</Col>
</div>
);
}
}

View File

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

View File

@@ -1,5 +1,6 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import { Col } from 'reactstrap';
import {coMarketTexts} from '../../../constants/coMarketConstants';
class PackagePrice extends Component {
@@ -38,34 +39,55 @@ class PackagePrice extends Component {
return price;
}
getFormatedFixedPrice(selectedAgreement, selectedOptions, selectedAdditionals) {
if (!selectedAgreement) {
return '-';
}
const finalPrice = this.getFinalPrice(
selectedAgreement,
selectedOptions,
selectedAdditionals,
'fixedExtra');
return `${finalPrice.toLocaleString()} ${this.props.currency}`
}
getFormatedRecurrentPrice(selectedAgreement, selectedOptions, selectedAdditionals) {
if (!selectedAgreement) {
return '-';
}
const recurrentExtra = this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'recurentExtra');
const servicesExtra = this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'servicesExtra');
return `${(recurrentExtra + servicesExtra).toLocaleString()} ${this.props.currency}`
}
render() {
const {currency, selectedAgreement, selectedOptions, selectedAdditionals} = this.props;
const {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()} {currency}
</td>
<td>
{(this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'recurentExtra')
+ this.getFinalPrice(selectedAgreement, selectedOptions, selectedAdditionals, 'servicesExtra')).toLocaleString()}
{' '}{currency}
</td>
</tr>
</tbody>
</table>
}
<div className="shop-package-price d-flex flex-column">
<div className="d-flex flex-grow-1 no-wrap">
<Col className="col-3 offset-6">
<h6>{coMarketTexts.labels.ON_DELIVERY}:</h6>
</Col>
<Col className="col-3">
<h6>{coMarketTexts.labels.MONTHLY}:</h6>
</Col>
</div>
<div className="d-flex flex-grow-1 no-wrap">
<Col className="col-3 offset-6">
<h4>
{this.getFormatedFixedPrice(selectedAgreement, selectedOptions, selectedAdditionals)}
</h4>
</Col>
<Col className="col-3">
<h4>
{this.getFormatedRecurrentPrice(selectedAgreement, selectedOptions, selectedAdditionals)}
</h4>
</Col>
</div>
</div>
);
}

View File

@@ -55,19 +55,50 @@
#shop-package-buy-info{
padding: 2rem;
.shop-package-pricing {
> div.row {
margin: 0 32px 0 0;
}
}
.shop-package-calculated-price {
font-weight: $font-weight;
}
.shop-package-options{
margin: 1rem 0;
border-bottom: 2px solid $title-color;
input[type=radio], input[type=checkbox] {
position: relative;
margin: 0 10px 0 0;
}
.dropdown-toggle {
border: 1px solid $hoverColor;
border-radius: $box-radius;
padding: 0.375rem 0.75rem;
cursor: pointer;
margin: 20px 0;
}
.shop-package-option {
&-selected {
background: $lightHoverColor;
}
&-header {
color: $font-light-color;
}
&-description {
padding-top: 0.5rem;
font-size: $font-size-msmall;
margin-left: 38px;
color: $font-light-color;
}
label {
cursor: pointer;
padding: 0.375rem 0.75rem;
margin-right: 0.855rem;
}
}
&-pricing {
border-bottom: 2px solid $title-color;
margin: 3rem 0;
label {
margin-right: 0 !important;
min-height: 3rem;
padding-right: 0 !important;
}
}
.shop-package-option:hover, label:hover, .dropdown:hover {
background: $hoverColor;
}
}
.not-available {
@@ -79,59 +110,19 @@
}
.price-info-btn {
color: $info-color;
cursor: pointer;
padding: 0.5rem;
width: 1.605rem;
padding: 0.4rem;
}
.add-to-cart-btn {
cursor: pointer;
color: $darkGreyColor;
background-color: $whiteColor;
border-color:$warmGreyColor;
color: #fff;
background-color: $accentColor;
border: none;
}
.selected-option {
background: $hoverColor;
}
.option-selection {
padding: 0.5rem 0;
margin: 0;
flex-wrap: nowrap;
> label {
cursor: pointer;
margin: 0;
flex-wrap: nowrap;
}
input.price-type-option {
position: relative;
margin: 0 10px 0 0;
}
span.option-name {
font-size: $font-size-small;
}
}
.option-selection:hover {
background: $footer-background;
color: $whiteColor;
}
.option-selection:hover .price-info-btn {
color: $whiteColor;
}
.option-description {
padding-top: 0.5rem;
font-size: $font-size-msmall;
height: 2rem;
}
.price-info-popover {
max-width: 50rem;
}
@@ -140,32 +131,11 @@
font-size: $font-size-msmall;
}
.selection-price{
background: $hoverColor;
display: inline-block;
border-radius: $box-radius;
font-size: $font-size-big;
font-weight: bold;
}
.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;
.shop-package-price{
margin: 1rem 0.855rem 1rem 0;
padding: 0.375rem 0.75rem;
h6 {
color: $font-light-color;
}
}
}

View File

@@ -38,10 +38,18 @@ export const fromWCOrder = (WCOrder) => {
payPeriod: packageLine['pay_period'],
shortDesc: packageLine['short_desc'],
dateCompleted: formatDate(packageLine['date_completed']),
additionalPackages: packageLine['additional_packages'].map(additionalPackage => ({
idPackage: additionalPackage.id,
packageName: additionalPackage.name,
})),
options: packageLine['options'].map(packageOption => ({
idPackage: packageOption.id,
packageName: packageOption.name,
})),
};
}),
deliveryAddress: formatAddress(WCOrder.shipping),
customer: WCOrder.customer,
commercialLead: WCOrder['commercial_lead']
commercialLead: WCOrder['commercial_lead'],
}
};

View File

@@ -1,6 +1,44 @@
const DEFAULT_PACKAGE_IMG = 'static/img/no-photo-package.jpg';
function extractPrices(wcPackageId, prices) {
return prices.map(price => ({
idPrice: price.id,
idPaymentType: price.id,
payType: price['payment_type'],
isSameCompanyAsCl: false,
idPackage: wcPackageId,
periodUnit: price['period_unit'],
maxContractPeriod: price['max_contract_period'],
packagePayPeriod: price['package_pay_period'],
servicesContractPeriod: price['services_contract_period'],
fixedExtra: price['minimal_fixed_price'],
servicesExtra: price['minimal_services_price'],
recurentExtra: price['recurrent_price']
}));
}
function extractGroups(wcPackageGroups) {
const extractedGroups = {};
Object.keys(wcPackageGroups).forEach(key => {
const group = wcPackageGroups[key];
extractedGroups[key] = {
groupName: group.name,
idGroup: group.id,
options: group.options.map(option => ({
idOptionPackage: option.id,
optionName: option.name,
isDefault: option.default,
shortDescription: option.description,
prices: extractPrices(option.id, option.prices),
})),
};
});
return extractedGroups;
}
export const fromWCPackage = wcPackage => {
return {
id: wcPackage.id,
@@ -11,22 +49,26 @@ export const fromWCPackage = wcPackage => {
country: 'Sweden',
countryCode: 'se',
currency: 'SEK',
documents: [],
documents: [
{
idDocument: 1,
documentName: 'test1',
extension: '.php'
},
{
idDocument: 2,
documentName: 'test2',
extension: '.php'
}
],
shortDescription: wcPackage.description,
prices: wcPackage.prices.map(price => ({
idPrice: price.id,
idPaymentType: price.id,
payType: price['payment_type'],
isSameCompanyAsCl: false,
idPackage: wcPackage.id,
periodUnit: price['period_unit'],
maxContractPeriod: price['max_contract_period'],
packagePayPeriod: price['package_pay_period'],
servicesContractPeriod: price['services_contract_period'],
fixedExtra: price['minimal_fixed_price'],
servicesExtra: price['minimal_services_price'],
recurentExtra: price['recurrent_price']
})) || [],
groups: [],
prices: extractPrices(wcPackage.id, wcPackage.prices) || [],
groups: extractGroups(wcPackage.groups),
additionalPackages: wcPackage['additional_packages'].map(additionalPackage =>({
idAdditionalPackage: additionalPackage.id,
packageName: additionalPackage.name,
shortDescription: additionalPackage.description,
prices: extractPrices(additionalPackage.id, additionalPackage.prices)
})),
}
};

View File

@@ -12,8 +12,10 @@ $ricohRed: #d2063a;
$greenColor: #34c388;
$shadow-color: rgba(0, 0, 0, 0.07);
$blueColor: #2b6279;
$hoverColor: #F8F8F8;
$lightHoverColor: rgba(126, 126, 126, 0.1);
$hoverColor: #ebebeb;
$lightHoverColor: #f2f2f2;
$accentColor: #f16078;
$boxShadow: rgba(0, 0, 0, 0.1);
@@ -37,6 +39,9 @@ $font-size-normal: 1rem; //16px
$font-size-big: 1.125rem; //18px
$font-size-xbig: 1.5rem;
$font-light-color: rgba(33, 33, 33, 0.54);
$font-strong-color: rgba(33, 33, 33, 0.87);
$open-status-color: #045FB4;
$in-progress-status-color: #FD8049;
$end-of-life-status-color: #34C388;//#800080
@@ -57,5 +62,3 @@ $warm-grey: #7e7e7e;
$info-color: #337ab7;
$danger: #F70D1A;
$warning: #FF8040;
$cart-count-color: #FFD3A1;