forked from M-Labs/web2019
886 lines
32 KiB
JavaScript
886 lines
32 KiB
JavaScript
import React, {PureComponent} from 'react';
|
|
import PropTypes from "prop-types";
|
|
import {FilterOptions} from "./options/utils";
|
|
import {v4 as uuidv4} from "uuid";
|
|
import {DragDropContext} from "@hello-pangea/dnd";
|
|
|
|
import {copy, itemsUnfoldedList, nbrOccupiedSlotsInCrate, remove, reorder} from "./utils";
|
|
|
|
import {Layout} from "./Layout.jsx";
|
|
import {Backlog} from "./Backlog.jsx";
|
|
import {OrderPanel} from "./OrderPanel.jsx";
|
|
import {CrateMode} from "./CrateMode.jsx";
|
|
import {Crate} from "./Crate.jsx";
|
|
import {Cart} from "./Cart.jsx";
|
|
import {OrderSummary} from "./OrderSummary.jsx";
|
|
import {OrderForm} from "./OrderForm.jsx";
|
|
|
|
/**
|
|
* Component that render the entire shop
|
|
*/
|
|
|
|
export class Shop extends PureComponent {
|
|
|
|
static get propTypes() {
|
|
return {
|
|
data: PropTypes.object.isRequired,
|
|
};
|
|
}
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = this.props.data;
|
|
this.handleCrateModeChange = this.handleCrateModeChange.bind(this);
|
|
this.handleOnDragEnd = this.handleOnDragEnd.bind(this);
|
|
this.handleDeleteItem = this.handleDeleteItem.bind(this);
|
|
this.handleDeleteAllItems = this.handleDeleteAllItems.bind(this);
|
|
this.handleMouseEnterItem = this.handleMouseEnterItem.bind(this);
|
|
this.handleMouseLeaveItem = this.handleMouseLeaveItem.bind(this);
|
|
this.handleClickAddItem = this.handleClickAddItem.bind(this);
|
|
this.checkAlerts = this.checkAlerts.bind(this);
|
|
this.handleClickSelectItem = this.handleClickSelectItem.bind(this);
|
|
this.handleClickSubmit = this.handleClickSubmit.bind(this);
|
|
this.handleToggleOverlayRemove = this.handleToggleOverlayRemove.bind(this);
|
|
this.handleShowOverlayRemove = this.handleShowOverlayRemove.bind(this);
|
|
this.handleClickToggleMobileSideMenu = this.handleClickToggleMobileSideMenu.bind(this);
|
|
this.handleClickCloseRFQFeedback = this.handleClickCloseRFQFeedback.bind(this);
|
|
this.handleClickShowOrder = this.handleClickShowOrder.bind(this);
|
|
this.handleClickOpenImport = this.handleClickOpenImport.bind(this);
|
|
this.handleLoadCustomConf = this.handleLoadCustomConf.bind(this);
|
|
this.handleCardsUpdated = this.handleCardsUpdated.bind(this);
|
|
|
|
this.timer = null;
|
|
this.timer_remove = null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
const source = {
|
|
droppableId: 'backlog',
|
|
indexes: [
|
|
itemsUnfoldedList.findIndex(element => element === "eem_pwr_mod"),
|
|
itemsUnfoldedList.findIndex(element => element === "kasli")
|
|
],
|
|
};
|
|
const destination = {
|
|
droppableId: 'cart',
|
|
index: 0,
|
|
};
|
|
|
|
this.handleOnDragEnd({
|
|
source,
|
|
destination
|
|
});
|
|
}
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
|
/**
|
|
* We check alerts (reminder + warning) only when items inside crate or
|
|
* crate mode change.
|
|
*
|
|
* In the function checkAlerts, we DO NOT want to change items as we will
|
|
* trigger again this function (componentDidUpdate) and thus,
|
|
* making an infinite loop.
|
|
*/
|
|
if (
|
|
(prevState.columns.cart.items !== this.state.columns.cart.items) ||
|
|
(prevState.currentMode !== this.state.currentMode)
|
|
) {
|
|
this.checkAlerts(this.state.columns.cart.items);
|
|
}
|
|
|
|
if (this.state.newCardJustAdded) {
|
|
this.timer = setTimeout(() => {
|
|
this.setState({
|
|
newCardJustAdded: false,
|
|
});
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
clearTimeout(this.timer);
|
|
}
|
|
|
|
handleCardsUpdated() {
|
|
this.checkAlerts(this.state.columns.cart.items);
|
|
}
|
|
|
|
handleCrateModeChange(mode) {
|
|
this.setState({
|
|
currentMode: mode,
|
|
});
|
|
}
|
|
|
|
handleDeleteItem(index) {
|
|
let cloned = Array.from(this.state.columns.cart.items);
|
|
let cloned_data = Array.from(this.state.columns.cart.itemsData);
|
|
cloned.splice(index, 1);
|
|
cloned_data.splice(index, 1);
|
|
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: cloned,
|
|
itemsData: cloned_data,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
handleDeleteAllItems() {
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: [],
|
|
itemsData: []
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
handleMouseEnterItem(id) {
|
|
this.setState({
|
|
...this.state,
|
|
currentItemHovered: id,
|
|
});
|
|
}
|
|
|
|
handleMouseLeaveItem() {
|
|
this.setState({
|
|
...this.state,
|
|
currentItemHovered: null,
|
|
});
|
|
}
|
|
|
|
handleClickAddItem(index, tap) {
|
|
const source = {
|
|
droppableId: 'backlog',
|
|
index: index,
|
|
};
|
|
const destination = {
|
|
droppableId: 'cart',
|
|
index: this.state.columns.cart.items.length,
|
|
};
|
|
|
|
this.handleOnDragEnd({
|
|
source,
|
|
destination
|
|
}, tap);
|
|
}
|
|
|
|
handleClickSelectItem(index) {
|
|
const itemsCloned = Array.from(this.state.columns.cart.items);
|
|
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: itemsCloned.map((item, id) => {
|
|
return {...item, selected: id === index ? true : false};
|
|
}),
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
handleToggleOverlayRemove(index, show) {
|
|
const itemsCloned = Array.from(this.state.columns.cart.items);
|
|
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: itemsCloned.map((item, id) => {
|
|
return {
|
|
...item,
|
|
showOverlayRemove: id === index ? show : false
|
|
};
|
|
}),
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
handleShowOverlayRemove(index) {
|
|
if (this.timer_remove)
|
|
clearTimeout(this.timer_remove);
|
|
|
|
this.handleToggleOverlayRemove(index, true);
|
|
|
|
this.timer_remove = setTimeout(() => {
|
|
this.handleToggleOverlayRemove(index, false);
|
|
}, 2000);
|
|
}
|
|
|
|
handleClickShowOrder() {
|
|
const crate = {
|
|
items: [],
|
|
type: this.state.currentMode,
|
|
};
|
|
const clonedCart = Array.from(this.state.columns.cart.items);
|
|
const clonedCartData = Array.from(this.state.columns.cart.itemsData);
|
|
for (const i in clonedCart) {
|
|
const item = clonedCart[i];
|
|
const item_data = clonedCartData[i];
|
|
crate.items.push({
|
|
'pn': item.name_number,
|
|
'options': (item_data.options_data && item_data.options) ? FilterOptions(item_data.options, item_data.options_data) : null,
|
|
});
|
|
}
|
|
|
|
this.setState({
|
|
isProcessing: false,
|
|
shouldShowRFQFeedback: true,
|
|
RFQBodyType: 'show',
|
|
RFQBodyOrder: JSON.stringify(crate, null, 2),
|
|
});
|
|
}
|
|
|
|
handleClickOpenImport() {
|
|
this.setState({
|
|
isProcessing: false,
|
|
shouldShowRFQFeedback: true,
|
|
RFQBodyType: 'import',
|
|
});
|
|
}
|
|
|
|
handleLoadCustomConf(customconf) {
|
|
if (!customconf) {return; }
|
|
|
|
const items = this.props.data.items;
|
|
|
|
let new_items = [];
|
|
let new_items_data = [];
|
|
|
|
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: [],
|
|
},
|
|
},
|
|
}, function () {
|
|
|
|
customconf.items.map(function (item) {
|
|
Object.keys(items).map(key => {
|
|
if (item.pn && item.pn === items[key].name_number) {
|
|
new_items.push(Object.assign({
|
|
...items[key],
|
|
}, {
|
|
id: uuidv4(),
|
|
options_data: item.options ? item.options : null,
|
|
}));
|
|
new_items_data.push({options_data: item.options ? item.options : null});
|
|
}
|
|
});
|
|
|
|
return item;
|
|
});
|
|
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
items: new_items,
|
|
itemsData: new_items_data,
|
|
},
|
|
},
|
|
currentMode: customconf.type,
|
|
});
|
|
});
|
|
}
|
|
|
|
handleClickSubmit(note, email) {
|
|
const crate = {
|
|
items: [],
|
|
type: this.state.currentMode,
|
|
};
|
|
const clonedCart = Array.from(this.state.columns.cart.items);
|
|
const clonedCartData = Array.from(this.state.columns.cart.itemsData);
|
|
for (const i in clonedCart) {
|
|
const item = clonedCart[i];
|
|
const item_data = clonedCartData[i];
|
|
crate.items.push({
|
|
'pn': item.name_number,
|
|
'options': (item_data.options_data && item_data.options) ? FilterOptions(item_data.options, item_data.options_data) : null,
|
|
});
|
|
}
|
|
|
|
const {data} = this.props;
|
|
|
|
this.setState({isProcessing: true});
|
|
|
|
fetch(data.API_RFQ, {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
email,
|
|
note,
|
|
configuration: JSON.stringify(crate)
|
|
})
|
|
}).then(response => {
|
|
if (response.status !== 200) {
|
|
throw Error("Response status is not OK: " + response.status + ".\n" + response);
|
|
}
|
|
this.setState({
|
|
isProcessing: false,
|
|
shouldShowRFQFeedback: true,
|
|
RFQBodyType: 'email',
|
|
isProcessingComplete: true,
|
|
});
|
|
}).catch(err => {
|
|
console.error("Request failed, reason:", err)
|
|
this.setState({isProcessing: false}, () => {
|
|
alert("We cannot receive your request. Try using the export by coping the configuration and send it to us at sales@m-labs.hk");
|
|
});
|
|
})
|
|
}
|
|
|
|
handleOnDragEnd(result, newAdded) {
|
|
const {
|
|
source,
|
|
destination,
|
|
} = result;
|
|
let dragged_items = [];
|
|
if (source.indexes) {
|
|
source.indexes.forEach((card_index, _) => {
|
|
dragged_items.push(itemsUnfoldedList[card_index]);
|
|
})
|
|
} else if (source.index >= 0) {
|
|
dragged_items.push(itemsUnfoldedList[source.index]);
|
|
}
|
|
|
|
|
|
if (!destination) {
|
|
if (source.droppableId === 'cart') {
|
|
this.setState({
|
|
...this.state,
|
|
newCardJustAdded: false,
|
|
columns: {
|
|
...this.state.columns,
|
|
[source.droppableId]: {
|
|
...this.state.columns[source.droppableId],
|
|
items: remove(
|
|
this.state.columns[source.droppableId].items,
|
|
source.index,
|
|
),
|
|
itemsData: remove(
|
|
this.state.columns[source.droppableId].itemsData,
|
|
source.index,
|
|
)
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
switch(source.droppableId) {
|
|
|
|
case 'backlog':
|
|
if (source.droppableId !== destination.droppableId) {
|
|
this.setState({
|
|
...this.state,
|
|
newCardJustAdded: newAdded ? true : false,
|
|
columns: {
|
|
...this.state.columns,
|
|
[destination.droppableId]: {
|
|
...this.state.columns[destination.droppableId],
|
|
items: copy(
|
|
this.state.items,
|
|
this.state.columns[source.droppableId],
|
|
this.state.columns[destination.droppableId],
|
|
dragged_items,
|
|
destination,
|
|
),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
break;
|
|
|
|
case destination.droppableId:
|
|
this.setState({
|
|
...this.state,
|
|
newCardJustAdded: false,
|
|
columns: {
|
|
...this.state.columns,
|
|
[destination.droppableId]: {
|
|
...this.state.columns[destination.droppableId],
|
|
items: reorder(
|
|
this.state.columns[destination.droppableId].items,
|
|
source.index,
|
|
destination.index,
|
|
),
|
|
itemsData: reorder(
|
|
this.state.columns[destination.droppableId].itemsData,
|
|
source.index,
|
|
destination.index,
|
|
),
|
|
},
|
|
},
|
|
});
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
handleClickToggleMobileSideMenu() {
|
|
this.setState({
|
|
...this.state,
|
|
mobileSideMenuShouldOpen: !this.state.mobileSideMenuShouldOpen,
|
|
});
|
|
}
|
|
|
|
handleClickCloseRFQFeedback() {
|
|
this.setState({
|
|
shouldShowRFQFeedback: false,
|
|
});
|
|
}
|
|
|
|
checkAlerts(newItems) {
|
|
console.log('--- START CHECKING CRATE WARNING ---');
|
|
|
|
const {
|
|
currentMode,
|
|
crateModeSlots,
|
|
crateRules,
|
|
} = this.state;
|
|
|
|
const itemsCloned = Array.from(newItems);
|
|
const itemsData = [];
|
|
const rules = {};
|
|
|
|
itemsCloned.forEach((elem, idx) => {
|
|
if (!(idx in itemsData)) itemsData[idx] = elem;
|
|
if (idx in this.state.columns.cart.itemsData && this.state.columns.cart.itemsData[idx].options_data) {
|
|
itemsCloned[idx].options_data = this.state.columns.cart.itemsData[idx].options_data;
|
|
}
|
|
itemsData[idx].warnings = {};
|
|
});
|
|
|
|
|
|
// check number of slot in crate
|
|
const nbrOccupied = nbrOccupiedSlotsInCrate(newItems);
|
|
if (nbrOccupied > crateModeSlots[currentMode]) {
|
|
rules[crateRules.maxSlot.type] = {...crateRules.maxSlot};
|
|
} else if (crateModeSlots[currentMode] === 21 && nbrOccupied <= 10) {
|
|
rules[crateRules.compactSlot.type] = {...crateRules.compactSlot};
|
|
}
|
|
|
|
|
|
// check the number of EEM connectors available for all Kasli
|
|
const idxK = itemsCloned.reduce((prev, next, i) => {
|
|
if (next.type === 'kasli' || next.type === 'vhdcicarrier') {
|
|
prev.push(i);
|
|
}
|
|
return prev;
|
|
}, []);
|
|
for (let i = 0; i <= idxK.length - 1; i++) {
|
|
let slots;
|
|
let nbUsedSlot = 0;
|
|
let nbrCurrentClock = 0;
|
|
let idx = idxK[i];
|
|
|
|
if (i !== idxK.length - 1) {
|
|
slots = itemsCloned.slice(idx + 1, idxK[i + 1]);
|
|
} else {
|
|
slots = itemsCloned.slice(idx + 1);
|
|
}
|
|
|
|
if (i == 0) {
|
|
const slots_need_resource = itemsCloned.slice(0, idx);
|
|
const idx_need = slots_need_resource.findIndex(e => (e.rules && e.rules.resources));
|
|
|
|
if (idx_need != -1) {
|
|
if (idx_need in itemsData) {
|
|
if ('warnings' in itemsData[idx_need]) {
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
} else {
|
|
itemsData[idx_need].warnings = {};
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
}
|
|
} else {
|
|
itemsData[idx_need] = {...itemsCloned[idx_need]};
|
|
itemsData[idx_need].warnings = {};
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
}
|
|
}
|
|
}
|
|
|
|
const process_slots = (item) => {
|
|
if (!item.options_data
|
|
|| item.options_data.ext_pwr === false
|
|
|| item.options_data.mono_eem === false
|
|
)
|
|
return item.slotOccupied;
|
|
else if (item.options_data.ext_pwr === true)
|
|
return 0;
|
|
else if (item.options_data.mono_eem === true || item.options_data.n_eem === "1 EEM")
|
|
return 1;
|
|
else if (item.options_data.n_eem === "3 EEM")
|
|
return 3;
|
|
|
|
return item.slotOccupied;
|
|
}
|
|
|
|
nbUsedSlot = slots
|
|
.filter(item => item.type !== 'idc-bnc')
|
|
.reduce((prev, next) => {
|
|
return prev + process_slots(next);
|
|
}, 0);
|
|
|
|
nbrCurrentClock = slots
|
|
.reduce((prev, next) => {
|
|
return next.type === 'clocker' ? prev + ((next.options_data && next.options_data.ext_clk === true) ? 0 : next.clockOccupied) : prev;
|
|
}, 0);
|
|
|
|
if (idx in itemsData) {
|
|
itemsData[idx].nbrCurrentSlot = nbUsedSlot;
|
|
itemsData[idx].nbrCurrentClock = nbrCurrentClock;
|
|
if (!('warnings' in itemsData[idx])) {
|
|
itemsData[idx].warnings = {};
|
|
}
|
|
} else {
|
|
itemsData[idx] = {...itemsCloned[idx]};
|
|
itemsData[idx].nbrCurrentSlot = nbUsedSlot;
|
|
itemsData[idx].nbrCurrentClock = nbrCurrentClock;
|
|
itemsData[idx].warnings = {};
|
|
}
|
|
|
|
if (nbUsedSlot > itemsCloned[idx].nbrSlotMax) {
|
|
if (itemsCloned[idx].rules.maxSlot.message) {
|
|
rules[itemsCloned[idx].rules.maxSlot.type] = {...itemsCloned[idx].rules.maxSlot};
|
|
}
|
|
itemsData[idx].warnings.maxSlotWarning = {...itemsCloned[idx].rules.maxSlotWarning};
|
|
}
|
|
|
|
if (nbrCurrentClock > itemsCloned[idx].nbrClockMax) {
|
|
rules[itemsCloned[idx].rules.maxClock.type] = {...itemsCloned[idx].rules.maxClock};
|
|
itemsData[idx].warnings.maxClockWarning = {...itemsCloned[idx].rules.maxClockWarning};
|
|
}
|
|
|
|
if (itemsCloned.length > (idx + 1)) {
|
|
const ddkali = itemsCloned[idx + 1];
|
|
if (ddkali.type === 'kasli' || ddkali.type === 'vhdcicarrier') {
|
|
rules[ddkali.rules.follow.type] = {...ddkali.rules.follow};
|
|
}
|
|
}
|
|
}
|
|
|
|
if (idxK.length === 0) {
|
|
const slots_need_resource = itemsCloned.slice(0);
|
|
const idx_need = slots_need_resource.findIndex(e => (e.rules && e.rules.resources));
|
|
|
|
if (idx_need != -1) {
|
|
if (idx_need in itemsData) {
|
|
if ('warnings' in itemsData[idx_need]) {
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
} else {
|
|
itemsData[idx_need].warnings = {};
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
}
|
|
} else {
|
|
itemsData[idx_need] = {...itemsCloned[idx_need]};
|
|
itemsData[idx_need].warnings = {};
|
|
itemsData[idx_need].warnings.resources = {...itemsCloned[idx_need].rules.resources};
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// check number of clock connector available
|
|
const idxC = itemsCloned.reduce((prev, next, i) => {
|
|
if (next.type === 'kasli' || next.type === 'clocker') {
|
|
prev.push(i);
|
|
}
|
|
return prev;
|
|
}, []);
|
|
for (let i = 0; i <= idxC.length - 1; i++) {
|
|
let slots;
|
|
let nbrCurrentClock = 0;
|
|
let idx = idxC[i];
|
|
|
|
if (i !== idxC.length - 1) {
|
|
slots = itemsCloned.slice(idx + 1, idxC[i + 1]);
|
|
} else {
|
|
slots = itemsCloned.slice(idx + 1);
|
|
}
|
|
|
|
nbrCurrentClock = slots.reduce((prev, next) => {
|
|
return prev + ((next.options_data && next.options_data.ext_clk && next.options_data.ext_clk.checked) ? 0 : next.clockOccupied);
|
|
}, 0);
|
|
|
|
if (idx in itemsData) {
|
|
if (itemsData[idx].nbrCurrentClock && itemsData[idx].type !== "clocker") {
|
|
itemsData[idx].nbrCurrentClock += nbrCurrentClock;
|
|
} else {
|
|
itemsData[idx].nbrCurrentClock = nbrCurrentClock;
|
|
}
|
|
} else {
|
|
itemsData[idx] = {...itemsCloned[idx]};
|
|
itemsData[idx].nbrCurrentClock = nbrCurrentClock;
|
|
itemsData[idx].warnings = {};
|
|
}
|
|
|
|
if (nbrCurrentClock > itemsCloned[idx].nbrClockMax) {
|
|
rules[itemsCloned[idx].rules.maxClock.type] = {...itemsCloned[idx].rules.maxClock};
|
|
itemsData[idx].warnings.maxClockWarning = {...itemsCloned[idx].rules.maxClockWarning};
|
|
}
|
|
}
|
|
|
|
|
|
if (itemsCloned.find(elem => elem.type === 'urukul')) {
|
|
if (this.state.items['urukul'].rules.info) {
|
|
rules[this.state.items['urukul'].rules.info.type] = {...this.state.items['urukul'].rules.info};
|
|
}
|
|
}
|
|
|
|
|
|
// check if IDC-BNC is correctly positionned (after Zotino or HD68)
|
|
const idxIDCBNC = itemsCloned.reduce((prev, next, i) => {
|
|
if (next.type === 'idc-bnc') {
|
|
prev.push(i);
|
|
}
|
|
return prev;
|
|
}, []);
|
|
for (var i = idxIDCBNC.length - 1; i >= 0; i--) {
|
|
const ce = idxIDCBNC[i];
|
|
let shouldWarning = false;
|
|
|
|
if (ce == 0) {
|
|
shouldWarning = true;
|
|
} else if (ce >= 1) {
|
|
const pe = idxIDCBNC[i] - 1;
|
|
if (itemsCloned[pe].type !== 'zotino' &&
|
|
itemsCloned[pe].type !== 'hd68' &&
|
|
itemsCloned[pe].type !== 'idc-bnc') {
|
|
shouldWarning = true;
|
|
}
|
|
}
|
|
|
|
if (shouldWarning) {
|
|
itemsData[ce] = {...itemsCloned[ce]};
|
|
itemsData[ce].warnings = {};
|
|
itemsData[ce].warnings.wrong = {...itemsCloned[ce].rules.wrong};
|
|
}
|
|
}
|
|
|
|
|
|
// check number of IDC-BNC adapters for a Zotino and HD68-IDC
|
|
const idxZH = itemsCloned.reduce((prev, next, i) => {
|
|
if (next.type === 'zotino' || next.type === 'hd68') {
|
|
prev.push(i);
|
|
}
|
|
return prev;
|
|
}, []);
|
|
for (let i = 0; i <= idxZH.length - 1; i++) {
|
|
let slots;
|
|
let nbUsedSlot = 0;
|
|
let idx = idxZH[i];
|
|
|
|
if (i !== idxZH.length - 1) {
|
|
slots = itemsCloned.slice(idx + 1, idxZH[i + 1]);
|
|
} else {
|
|
slots = itemsCloned.slice(idx + 1);
|
|
}
|
|
|
|
let stopCount = false;
|
|
nbUsedSlot = slots.reduce((prev, next, ci, ca) => {
|
|
if (ci === 0 && next.type === 'idc-bnc') {
|
|
return prev + 1;
|
|
} else if (ca[0].type === 'idc-bnc' && ci > 0 && ca[ci - 1].type === 'idc-bnc') {
|
|
if (next.type !== 'idc-bnc') { stopCount = true; }
|
|
return prev + (next.type === 'idc-bnc' && !stopCount ? 1 : 0);
|
|
}
|
|
return prev;
|
|
}, 0);
|
|
|
|
if (idx in itemsData) {
|
|
itemsData[idx].nbrCurrentSlot = nbUsedSlot;
|
|
if (!('warnings' in itemsData[idx])) {
|
|
itemsData[idx].warnings = {};
|
|
}
|
|
} else {
|
|
itemsData[idx] = {...itemsCloned[idx]};
|
|
itemsData[idx].nbrCurrentSlot = nbUsedSlot;
|
|
itemsData[idx].warnings = {};
|
|
}
|
|
|
|
if (nbUsedSlot > 0) {
|
|
if (itemsCloned[idx].rules.maxSlot.message) {
|
|
rules[itemsCloned[idx].rules.maxSlot.type] = {...itemsCloned[idx].rules.maxSlot};
|
|
}
|
|
}
|
|
if (nbUsedSlot > itemsCloned[idx].nbrSlotMax) {
|
|
itemsData[idx].warnings.maxSlotWarning = {...itemsCloned[idx].rules.maxSlotWarning};
|
|
}
|
|
|
|
// check if HD68-IDC has at least 1 IDC-BNC adapter
|
|
if (itemsCloned[idx].type === 'hd68') {
|
|
let shouldWarning = false;
|
|
|
|
if (idx < itemsCloned.length - 1) {
|
|
if (itemsCloned[idx + 1].type !== 'idc-bnc') {
|
|
shouldWarning = true;
|
|
}
|
|
} else if (idx === itemsCloned.length - 1) {
|
|
shouldWarning = true;
|
|
}
|
|
|
|
if (shouldWarning) {
|
|
if (idx in itemsData) {
|
|
itemsData[idx].warnings.minAdapter = {...itemsCloned[idx].rules.minAdapter};
|
|
} else {
|
|
itemsData[idx] = {...itemsCloned[idx]};
|
|
itemsData[idx].warnings = {};
|
|
itemsData[idx].warnings.minAdapter = {...itemsCloned[idx].rules.minAdapter};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// update state with rules
|
|
this.setState({
|
|
...this.state,
|
|
columns: {
|
|
...this.state.columns,
|
|
cart: {
|
|
...this.state.columns.cart,
|
|
itemsData: itemsData,
|
|
}
|
|
},
|
|
rules: {
|
|
...rules,
|
|
},
|
|
});
|
|
}
|
|
|
|
render() {
|
|
|
|
const {
|
|
currency,
|
|
currentItemHovered,
|
|
currentMode,
|
|
crateModeSlots,
|
|
crateModeItems,
|
|
items,
|
|
columns,
|
|
rules,
|
|
mobileSideMenuShouldOpen,
|
|
newCardJustAdded,
|
|
isProcessing,
|
|
shouldShowRFQFeedback,
|
|
RFQBodyType,
|
|
RFQBodyOrder,
|
|
isProcessingComplete,
|
|
} = this.state;
|
|
|
|
const isMobile = window.deviceIsMobile();
|
|
const isTouch = window.isTouchEnabled();
|
|
|
|
return (
|
|
<DragDropContext onDragEnd={this.handleOnDragEnd}>
|
|
|
|
<Layout
|
|
showRFQFeedback={shouldShowRFQFeedback}
|
|
RFQBodyType={RFQBodyType}
|
|
RFQBodyOrder={RFQBodyOrder}
|
|
className="shop"
|
|
mobileSideMenuShouldOpen={mobileSideMenuShouldOpen}
|
|
isMobile={isMobile}
|
|
newCardJustAdded={newCardJustAdded}
|
|
onClickToggleMobileSideMenu={this.handleClickToggleMobileSideMenu}
|
|
onClickCloseRFQFeedback={this.handleClickCloseRFQFeedback}
|
|
onClickLoadCustomConf={this.handleLoadCustomConf}
|
|
items={items}
|
|
aside={
|
|
<Backlog
|
|
currency={currency}
|
|
items={items}
|
|
data={columns['backlog']}
|
|
onClickAddItem={this.handleClickAddItem}
|
|
onClickToggleMobileSideMenu={this.handleClickToggleMobileSideMenu}
|
|
isMobile={isMobile}>
|
|
</Backlog>
|
|
}
|
|
main={(
|
|
<OrderPanel
|
|
onClickToggleMobileSideMenu={this.handleClickToggleMobileSideMenu}
|
|
onClickOpenImport={this.handleClickOpenImport}
|
|
isMobile={isMobile}
|
|
title="Order hardware"
|
|
description={(<p className="description">Drag and drop the cards you want into the crate below to see how the combination would look like. Setup card's configuration by tapping at the top of the card, most of the options can be modified after shipment. If you have any issues with this ordering system, or if you need other configurations, email us directly anytime at <a href="mailto:sales@m-labs.hk">sales@m-labs.hk</a>. The price is estimated and must be confirmed by a quote.</p>)}
|
|
crateMode={
|
|
<CrateMode
|
|
items={crateModeItems}
|
|
mode={currentMode}
|
|
onClickMode={this.handleCrateModeChange}>
|
|
</CrateMode>}
|
|
crate={
|
|
<Crate
|
|
cart={
|
|
<Cart
|
|
nbrSlots={crateModeSlots[currentMode]}
|
|
data={columns['cart']}
|
|
isMobile={isMobile}
|
|
isTouch={isTouch}
|
|
itemHovered={currentItemHovered}
|
|
onToggleOverlayRemove={this.handleToggleOverlayRemove}
|
|
onClickRemoveItem={this.handleDeleteItem}
|
|
onClickItem={this.handleShowOverlayRemove}
|
|
onCardUpdate={this.handleCardsUpdated}>
|
|
</Cart>
|
|
}
|
|
rules={Object.values(rules).filter(rule => rule)}>
|
|
</Crate>
|
|
}
|
|
summaryPrice={
|
|
<OrderSummary
|
|
currency={currency}
|
|
currentMode={currentMode}
|
|
modes={crateModeItems}
|
|
summary={columns['cart'].items}
|
|
itemsData={columns.cart.itemsData}
|
|
onMouseEnterItem={this.handleMouseEnterItem}
|
|
onMouseLeaveItem={this.handleMouseLeaveItem}
|
|
onDeleteItem={this.handleDeleteItem}
|
|
onDeleteAllItems={this.handleDeleteAllItems}
|
|
onClickSelectItem={this.handleClickSelectItem}>
|
|
</OrderSummary>
|
|
}
|
|
form={
|
|
<OrderForm
|
|
isProcessingComplete={isProcessingComplete}
|
|
processingComplete={this.handleProcessingComplete}
|
|
isProcessing={isProcessing}
|
|
onClickSubmit={this.handleClickSubmit}
|
|
onClickShow={this.handleClickShowOrder}>
|
|
</OrderForm>
|
|
}>
|
|
</OrderPanel>
|
|
)}>
|
|
</Layout>
|
|
|
|
</DragDropContext>
|
|
);
|
|
}
|
|
} |