'use strict'; import React from "react"; import axios from "axios"; import { createRoot } from "react-dom/client"; import PropTypes from "prop-types"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import { v4 as uuidv4 } from 'uuid'; import { OptionsDialogPopup, OptionsSummaryPopup, FilterOptions, FillExtData } from "./shop_components.jsx";import { OverlayTrigger } from "react-bootstrap"; const data = window.shop_data; const itemsUnfoldedList = Array.from(data.columns.backlog.categories.map(groupId => groupId.itemIds).flat()); const productStyle = (style, snapshot, removeAnim, hovered, selected, cart=false) => { const custom = { opacity: snapshot.isDragging ? .7 : 1, backgroundColor: (hovered || selected) ? '#eae7f7' : 'initial', }; if (!cart && snapshot.draggingOver == null && // hack for backlog ((!snapshot.isDragging) // prevent next elements from animation || (snapshot.isDragging && snapshot.isDropAnimating))) { // prevent dragged element from weird animation style.transform = "none"; } if (!snapshot.isDropAnimating) { return { ...style, ...custom}; } if (removeAnim) { // cannot be 0, but make it super tiny custom.transitionDuration = '0.001s'; } return { ...style, ...custom, }; } const cartStyle = (style, snapshot) => { const isDraggingOver = snapshot.isDraggingOver; return { ...style, ...{ backgroundColor: isDraggingOver ? '#f2f2f2' : '#f9f9f9', border: isDraggingOver ? '1px dashed #ccc' : '0', }, }; } const nbrConnectorsStyle = (data) => { if (!data || !data.nbrCurrentSlot) { return {}; } let p = data.nbrCurrentSlot * 100 / data.nbrSlotMax; if (p > 100) { p = 100; } return { width: `${p}%`, } }; const nbrClocksStyle = (data) => { if (!data || !data.nbrCurrentClock) { return {}; } let p = data.nbrCurrentClock * 100 / data.nbrClockMax; if (p > 100) { p = 100; } return { width: `${p}%`, } }; const copy = ( model, source, destination, draggableSource, droppableDestination ) => { const destClone = Array.from(destination.items); destClone.splice(droppableDestination.index, 0, ...draggableSource.map((dragged_item, _) => { return { ...model[dragged_item], id: uuidv4(), } })); return destClone; }; const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); return result; }; const remove = (list, startIndex) => { const result = Array.from(list); result.splice(startIndex, 1); return result; }; const nbrOccupiedSlotsInCrate = (items) => { return items.reduce((prev, next) => { return prev + (next.hp === 8 ? 2 : 1); }, 0); }; function formatMoney(amount, decimalCount = 2, decimal = ".", thousands = ",") { // https://stackoverflow.com/questions/149055/how-can-i-format-numbers-as-currency-string-in-javascript // changes: return amount if error in order to avoid empty value try { decimalCount = Math.abs(decimalCount); decimalCount = isNaN(decimalCount) ? 2 : decimalCount; const negativeSign = amount < 0 ? "-" : ""; let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString(); let j = (i.length > 3) ? i.length % 3 : 0; return negativeSign + (j ? i.substr(0, j) + thousands : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : ""); } catch (e) { return amount; } }; /** * Component that provides a base layout (aside/main) for the page. */ class Layout extends React.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 (
{mobileSideMenuShouldOpen ? (
{main}
) : (
{main}
)} {isMobile && newCardJustAdded ? (
✓ added
) : null}