From ec2c0a3b80748ad6d5bf80810ee4761d69994acd Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Mon, 11 Dec 2023 17:47:16 +0800 Subject: [PATCH] Somewhat minimal working example Signed-off-by: Egor Savkin --- static/js/shop/Cart.jsx | 2 + static/js/shop/OrderPanel.jsx | 8 +- static/js/shop/ProductCartItem.jsx | 8 +- static/js/shop/Shop.jsx | 590 ++--------------------------- static/js/shop/shop_store.js | 15 +- static/js/shop/warnings.js | 4 +- 6 files changed, 54 insertions(+), 573 deletions(-) diff --git a/static/js/shop/Cart.jsx b/static/js/shop/Cart.jsx index 2b180aa..965b9ce 100644 --- a/static/js/shop/Cart.jsx +++ b/static/js/shop/Cart.jsx @@ -17,6 +17,8 @@ export function Cart({crate_index}) { crate: state.crates[crate_index] })); + console.log(resource_counters, crate) + const nbrOccupied = hp_to_slots(resource_counters.hp(crate.items, -1)); const nbrSlots = hp_to_slots(crate_type_to_hp(crate.crate_mode)); console.log(nbrOccupied, nbrSlots); diff --git a/static/js/shop/OrderPanel.jsx b/static/js/shop/OrderPanel.jsx index bb77050..ada7a91 100644 --- a/static/js/shop/OrderPanel.jsx +++ b/static/js/shop/OrderPanel.jsx @@ -1,7 +1,7 @@ import React from 'react' -import {OrderSummary} from "./OrderSummary"; -import {OrderForm} from "./OrderForm"; -import {CrateList} from "./CrateList"; +import {OrderSummary} from "./OrderSummary.jsx"; +import {OrderForm} from "./OrderForm.jsx"; +import {CrateList} from "./CrateList.jsx"; import {useShopStore} from "./shop_store"; /** @@ -40,7 +40,7 @@ export function OrderPanel({title, description}) {
- +
diff --git a/static/js/shop/ProductCartItem.jsx b/static/js/shop/ProductCartItem.jsx index 0e5a928..429e9ca 100644 --- a/static/js/shop/ProductCartItem.jsx +++ b/static/js/shop/ProductCartItem.jsx @@ -24,10 +24,10 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last, let options, options_data; //const warnings = data && data.show_warnings; - if (data && data.options) { - options = data.options; - if (!data.options_data) crate.options_data = {}; - options_data = crate.options_data; + if (card && card.options) { + options = card.options; + if (!card.options_data) card.options_data = {}; + options_data = card.options_data; options_data.ext_data = ext_data; } diff --git a/static/js/shop/Shop.jsx b/static/js/shop/Shop.jsx index ef8d1e1..d3c12f4 100644 --- a/static/js/shop/Shop.jsx +++ b/static/js/shop/Shop.jsx @@ -1,577 +1,49 @@ -import React, {PureComponent} from 'react'; -import PropTypes from "prop-types"; -import {FilterOptions} from "./options/utils"; -import {v4 as uuidv4} from "uuid"; +import React from 'react'; import {DragDropContext} from "@hello-pangea/dnd"; -import {copyFromBacklog, itemsUnfoldedList, nbrOccupiedSlotsInCrate, remove, reorder} from "./utils"; import {Layout} from "./Layout.jsx"; import {Backlog} from "./Backlog.jsx"; import {OrderPanel} from "./OrderPanel.jsx"; -import {OrderSummary} from "./OrderSummary.jsx"; -import {OrderForm} from "./OrderForm.jsx"; -import {TriggerCrateWarnings, TriggerWarnings} from "./warnings"; -import {FillResources} from "./count_resources"; -import {CrateList} from "./CrateList.jsx"; +import {useShopStore} from "./shop_store"; /** * Component that render the entire shop */ export function Shop() { - - static get propTypes() { - return { - data: PropTypes.object.isRequired, - }; + const {addCardFromBacklog, moveCard, deleteCard} = useShopStore(state => ({ + addCardFromBacklog: state.addCardFromBacklog, + moveCard: state.moveCard, + deleteCard: state.deleteCard + })); + const handleOnDragEnd = (drop_result, provided) => { + console.log(drop_result, provided) + //{ + // "draggableId": "42dc17e9-9e75-45ee-ad27-2233b6f07a5e", + // "type": "DEFAULT", + // "source": { + // "index": 17, + // "droppableId": "backlog" + // }, + // "reason": "DROP", + // "mode": "FLUID", + // "destination": { + // "droppableId": "crate0", + // "index": 0 + // }, + // "combine": null + // } + if (drop_result.source.droppableId === "backlog") + addCardFromBacklog(drop_result.destination.droppableId, drop_result.source.index, drop_result.destination.index); + else if(drop_result.destination.droppableId === "backlog") + deleteCard(drop_result.destination.droppableId, drop_result.destination.index); + else + moveCard(drop_result.source.droppableId, drop_result.source.index, drop_result.destination.droppableId, drop_result.destination.index) } - 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.checkAlertsInNewState = this.checkAlertsInNewState.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.onAddCrate = this.onAddCrate.bind(this); - this.onDeleteCrate = this.onDeleteCrate.bind(this); - this.onCrateModeChange = this.onCrateModeChange.bind(this); - this.onCrateSelected = this.onCrateSelected.bind(this); - this.onCrateChanged = this.onCrateChanged.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: 'crate0', - 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. - */ - console.log("componentDidUpdate") - return; - if ( - (prevState.columns.crates !== this.state.columns.crates.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); - } - - onCrateChanged(crate_id) { - // kinda silly that hover over cards triggers checkAlerts - - this.checkAlertsInNewState(crate_id, this.state); - - } - - 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) { - /** - * 4 cases: - * - from backlog to one of the crate - add to the correct crate in correct order - * - from one crate to another - delete from one crate and put to another in correct order - * - within one crate - reorder - * - from crate to backlog - delete - * */ - - const { - source, - destination, - } = result; - /* better move to another function - 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]); - }*/ - - console.log('==> result', result); - - // dropped outside the list - if (!destination) { - return; - } - - switch (source.droppableId) { - // TODO add delete functionality - case destination.droppableId: - /*this.setState({ - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [destination.droppableId]: { - ...this.state.columns.crates[destination.droppableId], - items: reorder( - this.state.columns.crates[source.droppableId].items, - source.index, - destination.index - )}} - } - });*/ - this.checkAlertsInNewState(destination.droppableId, { - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [destination.droppableId]: { - ...this.state.columns.crates[destination.droppableId], - items: reorder( - this.state.columns.crates[source.droppableId].items, - source.index, - destination.index - )}} - } - }); - break; - case 'backlog': - /*this.setState({ - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [destination.droppableId]: { - ...this.state.columns.crates[destination.droppableId], - items: copyFromBacklog( - this.state.items, - this.state.columns.crates[destination.droppableId], - source, - destination - )}} - } - });*/ - this.checkAlertsInNewState(destination.droppableId, { - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [destination.droppableId]: { - ...this.state.columns.crates[destination.droppableId], - items: copyFromBacklog( - this.state.items, - this.state.columns.crates[destination.droppableId], - source, - destination - )}} - } - }); - break; - default: - this.setState({ - columns: move( - this.state.columns[source.droppableId], - this.state.columns[destination.droppableId], - source, - destination - ) - }); - break; - } - } - - handleClickToggleMobileSideMenu() { - this.setState({ - ...this.state, - mobileSideMenuShouldOpen: !this.state.mobileSideMenuShouldOpen, - }); - } - - handleClickCloseRFQFeedback() { - this.setState({ - shouldShowRFQFeedback: false, - }); - } - - checkAlertsInNewState(crate_id, new_state) { - console.log('--- START CHECKING CRATE WARNING ---'); - - let itemsCloned = Array.from(new_state.columns.crates[crate_id].items); - - const crate_warnings = TriggerCrateWarnings(new_state.columns.crates[crate_id]); - - itemsCloned.forEach((elem, _idx) => { - if (!elem.options_data && !!elem.options) { - elem.options_data = {} - } - }); - - itemsCloned = FillResources(itemsCloned); - itemsCloned = TriggerWarnings(itemsCloned); - - // update state with rules - this.setState({ - ...this.state, - columns: { - ...new_state.columns, - crates: { - ...new_state.columns.crates, - [crate_id]: { - ...new_state.columns.crates[crate_id], - items: itemsCloned, - warnings: crate_warnings - }, - } - } - }); - } - - onAddCrate(id) { - this.setState({ - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [id]: { - crate_type: "rack", - items: [] - }} - } - }); - } - onDeleteCrate(id) { - let new_state = { - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [id]: undefined - } - }}; - delete new_state.columns.crates[id]; - this.setState(new_state); - } - - onCrateSelected(id) { - console.log(id) - this.setState({ - active_crate: id - }) - } - - onCrateModeChange(crate_id, new_mode) { - let new_state = { - ...this.state, - columns: { - ...this.state.columns, - crates: { - ...this.state.columns.crates, - [crate_id]: { - ...this.state.columns.crates[crate_id], - crate_type: new_mode - } - } - }}; - this.setState(new_state); - } - - return ( - + diff --git a/static/js/shop/shop_store.js b/static/js/shop/shop_store.js index 124ff57..96061d5 100644 --- a/static/js/shop/shop_store.js +++ b/static/js/shop/shop_store.js @@ -93,14 +93,21 @@ const useCart = ((set, get) => ({ } }), moveCard: (crate_from, index_from, crate_to, index_to) => set(state => { - const the_card = state.crates.find((crate, _) => crate_from === crate.id )[index_from]; - const del_card = crate_from === crate_to ? 1 : 0; + const the_card = state.crates.find((crate, _) => crate_from === crate.id ).items[index_from]; return { crates: state.crates.map((crate, _i) => { - if (crate_to === crate.id) { + if (crate_to === crate_from && crate_to === crate.id) { + //const the_card = {...crate[index_from]}; + let items_copy = Array.from(crate.items); + delete items_copy[index_from]; return { ...crate, - items: crate.items.toSpliced(index_to, del_card, the_card) + items: items_copy.toSpliced(index_to, 0, the_card).filter((item, _) => !!item) + } + } else if (crate_to === crate.id) { + return { + ...crate, + items: crate.items.toSpliced(index_to, 0, the_card) } } else if (crate_from === crate.id) { return { diff --git a/static/js/shop/warnings.js b/static/js/shop/warnings.js index beaca90..fc325ce 100644 --- a/static/js/shop/warnings.js +++ b/static/js/shop/warnings.js @@ -100,7 +100,7 @@ const Types = { export function TriggerCardWarnings(data, index, precounted) { const element = data[index]; - return element.warnings + return (element.warnings && element.warnings .map((warning, _) => { if (!!Types[warning]) return Types[warning].trigger(data, index, precounted) ? {trigger: undefined, name: warning, ...Types[warning]} : null; @@ -109,7 +109,7 @@ export function TriggerCardWarnings(data, index, precounted) { }) .filter((warning, _) => { return !!warning - }); + })); } export function TriggerWarnings(data) {