273 lines
9.8 KiB
React
273 lines
9.8 KiB
React
|
import React, {PureComponent} from 'react';
|
||
|
import PropTypes from "prop-types";
|
||
|
|
||
|
/**
|
||
|
* Component that provides a base layout (aside/main) for the page.
|
||
|
*/
|
||
|
export class Layout extends PureComponent {
|
||
|
|
||
|
static get propTypes() {
|
||
|
return {
|
||
|
aside: PropTypes.any,
|
||
|
main: PropTypes.any,
|
||
|
mobileSideMenuShouldOpen: PropTypes.bool,
|
||
|
isMobile: PropTypes.bool,
|
||
|
newCardJustAdded: PropTypes.bool,
|
||
|
onClickToggleMobileSideMenu: PropTypes.func,
|
||
|
onClickCloseRFQFeedback: PropTypes.func,
|
||
|
RFQBodyType: PropTypes.string,
|
||
|
RFQBodyOrder: PropTypes.string,
|
||
|
onClickLoadCustomConf: PropTypes.func,
|
||
|
items: PropTypes.object,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
static get defaultProps() {
|
||
|
return {
|
||
|
mobileSideMenuShouldOpen: false,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
constructor(props) {
|
||
|
super(props);
|
||
|
|
||
|
this.state = {
|
||
|
customconf: '',
|
||
|
error: null,
|
||
|
};
|
||
|
|
||
|
this.handleCustomConfig = this.handleCustomConfig.bind(this);
|
||
|
this.handleClickLoad = this.handleClickLoad.bind(this);
|
||
|
this.checkValidation = this.checkValidation.bind(this);
|
||
|
|
||
|
// retrieve list of available pn
|
||
|
const items_keys = Object.keys(props.items);
|
||
|
this.list_pn = items_keys.map(function (key) {
|
||
|
return props.items[key].name_number;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
handleCustomConfig(e) {
|
||
|
const value = e.target.value;
|
||
|
|
||
|
this.checkValidation(value);
|
||
|
}
|
||
|
|
||
|
checkValidation(conf) {
|
||
|
let conf_obj;
|
||
|
|
||
|
try {
|
||
|
conf_obj = JSON.parse(conf);
|
||
|
} catch (e) {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: 'invalid format',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (!conf_obj) {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: 'invalid format',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if ((!conf_obj.items || !conf_obj.type) &&
|
||
|
(Object.prototype.toString.call(conf_obj.items) !== '[object Array]' ||
|
||
|
Object.prototype.toString.call(conf_obj.type) !== '[object String]')) {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: 'invalid format',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (conf_obj.type !== "desktop" && conf_obj.type !== "rack") {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: 'invalid format',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
conf_obj.items.map(function (item) {
|
||
|
try {
|
||
|
return JSON.parse(item);
|
||
|
} catch (e) {
|
||
|
return null;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
conf_obj.items = conf_obj.items.filter(function (item) {
|
||
|
return item;
|
||
|
});
|
||
|
|
||
|
if (conf_obj.items.filter(function (item) {
|
||
|
return Object.prototype.toString.call(item) !== '[object Object]' || !item.pn || Object.prototype.toString.call(item.pn) !== '[object String]';
|
||
|
}).length > 0) {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: 'invalid format',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
conf_obj.items = conf_obj.items.map(function (item) {
|
||
|
return {
|
||
|
pn: item.pn,
|
||
|
options: item.options ? item.options : null,
|
||
|
};
|
||
|
});
|
||
|
|
||
|
const self = this;
|
||
|
const unknow_pn = conf_obj.items.filter(function (item_pn) {
|
||
|
return self.list_pn.includes(item_pn.pn) === false;
|
||
|
}).map(function (item_pn) {
|
||
|
return item_pn.pn;
|
||
|
});
|
||
|
|
||
|
if (unknow_pn.length > 0) {
|
||
|
return this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
customconf_ready: null,
|
||
|
error: `${unknow_pn.join(', ')} unknown${unknow_pn.length > 1 ? 's':''} pn number`,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.setState({
|
||
|
...this.state,
|
||
|
customconf: conf,
|
||
|
error: null,
|
||
|
customconf_ready: conf_obj,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
handleClickLoad() {
|
||
|
this.checkValidation(this.state.customconf);
|
||
|
|
||
|
if (this.props.onClickLoadCustomConf) {
|
||
|
this.props.onClickLoadCustomConf(this.state.customconf_ready);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
const {
|
||
|
aside,
|
||
|
main,
|
||
|
mobileSideMenuShouldOpen,
|
||
|
isMobile,
|
||
|
newCardJustAdded,
|
||
|
onClickToggleMobileSideMenu,
|
||
|
onClickCloseRFQFeedback,
|
||
|
showRFQFeedback,
|
||
|
RFQBodyType,
|
||
|
RFQBodyOrder,
|
||
|
} = this.props;
|
||
|
|
||
|
return (
|
||
|
<div className="layout">
|
||
|
|
||
|
<aside className={'aside ' + (mobileSideMenuShouldOpen ? 'menu-opened' : '')}>{aside}</aside>
|
||
|
|
||
|
{mobileSideMenuShouldOpen ? (
|
||
|
<section className="main" onClick={onClickToggleMobileSideMenu}>{main}</section>
|
||
|
) : (
|
||
|
<section className="main">{main}</section>
|
||
|
)}
|
||
|
|
||
|
{isMobile && newCardJustAdded ? (
|
||
|
<div className="feedback-add-success">
|
||
|
✓ added
|
||
|
</div>
|
||
|
) : null}
|
||
|
|
||
|
|
||
|
<div className={`modal fade ${ showRFQFeedback ? 'show': ''}`} style={{'display': showRFQFeedback ? 'block':'none'}} id="exampleModal" tabIndex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||
|
<div className="modal-dialog" role="document">
|
||
|
<div className="modal-content">
|
||
|
<div className="modal-body rfqFeedback">
|
||
|
|
||
|
<div className="d-flex w-100">
|
||
|
|
||
|
{RFQBodyType === 'email' ? (
|
||
|
<div className="d-flex">
|
||
|
|
||
|
<div>
|
||
|
<img width="30px" src="/images/shop/icon-done.svg" alt="close" />
|
||
|
</div>
|
||
|
|
||
|
<div style={{'padding': '0 .5em'}}>
|
||
|
We've received your request and will be in contact soon.
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
) : null }
|
||
|
|
||
|
{RFQBodyType === 'show' ? (
|
||
|
<p>
|
||
|
{RFQBodyOrder}
|
||
|
</p>
|
||
|
) : null}
|
||
|
|
||
|
{RFQBodyType === 'import' ? (
|
||
|
<div className="w-100">
|
||
|
|
||
|
<form className="w-100">
|
||
|
<div className="mb-3">
|
||
|
<p className="small">
|
||
|
Input the JSON description below. Should be something like:
|
||
|
<br />
|
||
|
{JSON.stringify({"items":[{"pn":"1124"},{"pn":"2118"},{"pn":"2118"},{"pn":"2128"}],"type":"desktop"})}
|
||
|
</p>
|
||
|
</div>
|
||
|
|
||
|
<div className="mb-3 w-100">
|
||
|
<textarea
|
||
|
onChange={this.handleCustomConfig}
|
||
|
value={this.state.customconf}
|
||
|
className="form-control w-100"
|
||
|
rows="5"
|
||
|
placeholder="Input JSON description here." />
|
||
|
</div>
|
||
|
{this.state.error ? (
|
||
|
<div className="mb-3">
|
||
|
<p className="text-danger">{this.state.error}</p>
|
||
|
</div>
|
||
|
) : null}
|
||
|
</form>
|
||
|
|
||
|
<div className="d-flex flex-column flex-sm-row justify-content-end">
|
||
|
<a type="button" onClick={onClickCloseRFQFeedback} className="btn btn-sm btn-outline-primary m-0 mb-2 mb-sm-0 me-sm-2">Close</a>
|
||
|
<a type="button" onClick={this.handleClickLoad} className={`btn btn-sm btn-primary m-0 ms-sm-2 ${this.state.error ? 'disabled':''}`}>Load configuration</a>
|
||
|
</div>
|
||
|
</div>
|
||
|
) : null}
|
||
|
|
||
|
<div>
|
||
|
<button onClick={onClickCloseRFQFeedback}>
|
||
|
<img src="/images/shop/icon-close.svg" alt="close" />
|
||
|
</button>
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div onClick={onClickCloseRFQFeedback} className={`modal-backdrop fade ${ showRFQFeedback ? 'show': ''}`} style={{'display': showRFQFeedback ? 'initial':'none'}}></div>
|
||
|
|
||
|
</div>
|
||
|
);
|
||
|
}
|
||
|
}
|