From 3dc1cbc6a2103e349bf7dc9acecc5d2a9c404eb8 Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Fri, 1 Nov 2024 17:32:20 +0800 Subject: [PATCH] WIP add horizontal items into the cart TODO Cart FakePlaceholder logic Signed-off-by: Egor Savkin --- sass/css/_shop.scss | 38 +++++- static/images/shop/icon-drag-and-drop.svg | 1 + static/js/shop/Cart.jsx | 2 - static/js/shop/CartHorizontal.jsx | 59 +++++++++ static/js/shop/Crate.jsx | 9 +- static/js/shop/OptionsWrapper.jsx | 27 ++-- static/js/shop/ProductCartItem.jsx | 11 +- static/js/shop/ProductCartItemHorizontal.jsx | 98 ++++++++++++++ static/js/shop/ProductItem.jsx | 7 +- static/js/shop/SummaryCrate.jsx | 9 ++ static/js/shop/SummaryCrateCard.jsx | 34 +++-- static/js/shop/json_porter.js | 14 +- static/js/shop/shop_store.js | 129 ++++++++++++------- static/js/shop_data.js | 4 +- 14 files changed, 357 insertions(+), 85 deletions(-) create mode 100644 static/images/shop/icon-drag-and-drop.svg create mode 100644 static/js/shop/CartHorizontal.jsx create mode 100644 static/js/shop/ProductCartItemHorizontal.jsx diff --git a/sass/css/_shop.scss b/sass/css/_shop.scss index 84b6c1e..006db39 100644 --- a/sass/css/_shop.scss +++ b/sass/css/_shop.scss @@ -121,6 +121,10 @@ button { height: 400px; align-self: center; border: 0; + cursor: grab; + &.default-icon { + height: 144px; + } } h3 { @@ -487,8 +491,40 @@ button { &.horizontal { flex-direction: column; - min-height: 50px; + min-height: 10px; overflow-y: auto; + overflow-x: hidden; + width: 100%; + + > div { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-content: center; + align-items: center; + width: 100%; + max-width: 100%; + padding: 5px 3px; + } + .progress-container { + max-width: 40px; + padding: 0 10px; + justify-self: start; + } + .product-name { + justify-self: start; + padding: 0 10px; + max-width: 100%; + width: 100%; + } + .removeHorizontal { + justify-self: end; + padding: 0 10px; + img { + width: 20px; + height: 20px; + } + } } > div { diff --git a/static/images/shop/icon-drag-and-drop.svg b/static/images/shop/icon-drag-and-drop.svg new file mode 100644 index 0000000..1601063 --- /dev/null +++ b/static/images/shop/icon-drag-and-drop.svg @@ -0,0 +1 @@ + diff --git a/static/js/shop/Cart.jsx b/static/js/shop/Cart.jsx index 1876a35..33e7768 100644 --- a/static/js/shop/Cart.jsx +++ b/static/js/shop/Cart.jsx @@ -32,8 +32,6 @@ export function Cart({crate_index}) { = nbrSlots} key={item.id}/> ); }); diff --git a/static/js/shop/CartHorizontal.jsx b/static/js/shop/CartHorizontal.jsx new file mode 100644 index 0000000..41e5e76 --- /dev/null +++ b/static/js/shop/CartHorizontal.jsx @@ -0,0 +1,59 @@ +import React from 'react' +import {Droppable} from "@hello-pangea/dnd"; +import {cartStyle, compareArraysWithIds} from "./utils"; +import {ProductCartItemHorizontal} from "./ProductCartItemHorizontal"; +import {HORIZONTAL_CART_MARKER, useShopStore} from "./shop_store"; + +// #!render_count +import {useRenderCount} from "@uidotdev/usehooks"; + + +/** + * Component that displays a list of + */ +export function CartHorizontal({crate_index}) { + // #!render_count + const renderCount = useRenderCount(); + + const crate = useShopStore((state) => state.crates[crate_index], (a, b) => { + return compareArraysWithIds(a.h_items, b.h_items) + }); + + // #!render_count + console.log("CartHorizontal renders: ", renderCount) + + + const products = crate.h_items.map((item, index) => { + return ( + + ); + }); + + return ( + + {(provided, snapshot) => ( +
+ + {products} + + {provided.placeholder && ( +
+ {provided.placeholder} +
+ )} +
+ )} + +
+ ); +} \ No newline at end of file diff --git a/static/js/shop/Crate.jsx b/static/js/shop/Crate.jsx index 97eb454..e274652 100644 --- a/static/js/shop/Crate.jsx +++ b/static/js/shop/Crate.jsx @@ -4,6 +4,7 @@ import {CrateMode} from "./CrateMode"; import {CrateWarnings} from "./CrateWarnings"; import {useShopStore} from "./shop_store"; import {CrateOptions} from "./CrateOptions"; +import {CartHorizontal} from "./CartHorizontal"; // #!render_count import {useRenderCount} from "@uidotdev/usehooks"; @@ -28,7 +29,7 @@ export function Crate({crate_index}) { return (
{ - modes_order.includes(crate.crate_mode) ? ( + modes_order.includes(crate.crate_mode) && (
@@ -36,13 +37,17 @@ export function Crate({crate_index}) { Delete crate remove
- ) : <> + ) }
+ { !modes_order.includes(crate.crate_mode) && + + } + diff --git a/static/js/shop/OptionsWrapper.jsx b/static/js/shop/OptionsWrapper.jsx index 9105955..57315b4 100644 --- a/static/js/shop/OptionsWrapper.jsx +++ b/static/js/shop/OptionsWrapper.jsx @@ -1,21 +1,23 @@ import {DialogPopup} from "./options/DialogPopup"; import React from "react"; -import {useShopStore} from "./shop_store"; +import {useShopStore, whichItems} from "./shop_store"; import {SummaryPopup} from "./options/SummaryPopup"; export function OptionsDialogWrapper({crate_index, card_index, horizontal}) { + const whichH = whichItems(horizontal); const crate_id = useShopStore((state) => state.crates[crate_index].id); const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options); - const options = useShopStore((state) => state.crates[crate_index].items[card_index][use_options]); - const options_data = useShopStore((state) => state.crates[crate_index].items[card_index].options_data); - const card_id = useShopStore((state) => state.crates[crate_index].items[card_index].id); - const options_class = useShopStore((state) => state.crates[crate_index].items[card_index].options_class); + const options = useShopStore((state) => state.crates[crate_index][whichH][card_index][use_options]); + const options_data = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_data); + const card_id = useShopStore((state) => state.crates[crate_index][whichH][card_index].id); + const options_class = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_class); const sideMenuIsOpen = useShopStore((state) => state.sideMenuIsOpen); const _notificationTimer = useShopStore((state) => state.notificationTimer); const hideNotification = useShopStore((state) => state.hideNotification); - const displayNotification = useShopStore((state) => + const displayNotification = useShopStore((state) => + !!state.notificationHorizontal === !!horizontal && state.notificationCrateId === crate_id && - (state.notificationCardIndex === card_index || (state.crates[crate_index].items.length + (state.notificationCardIndex || -1)) === card_index)); + (state.notificationCardIndex === card_index || (state.crates[crate_index][whichH].length + (state.notificationCardIndex || -1)) === card_index)); const onOptionsUpdate = useShopStore((state) => state.updateOptions); @@ -42,18 +44,19 @@ export function OptionsDialogWrapper({crate_index, card_index, horizontal}) { console.log("update", outvar, value, options_data); if (outvar in options_data) options_data[outvar] = value; - onOptionsUpdate(crate_id, card_index, {[outvar]: value}); + onOptionsUpdate(crate_id, card_index, {[outvar]: value}, horizontal); }) }} /> ) } -export function OptionsSummaryWrapper({crate_index, card_index}) { - const card_id = useShopStore((state) => state.crates[crate_index].items[card_index].id); +export function OptionsSummaryWrapper({crate_index, card_index, horizontal}) { + const whichH = whichItems(horizontal); + const card_id = useShopStore((state) => state.crates[crate_index][whichH][card_index].id); const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options); - const options = useShopStore((state) => state.crates[crate_index].items[card_index][use_options]); - const options_data = useShopStore((state) => state.crates[crate_index].items[card_index].options_data); + const options = useShopStore((state) => state.crates[crate_index][whichH][card_index][use_options]); + const options_data = useShopStore((state) => state.crates[crate_index][whichH][card_index].options_data); return ( state.crates[crate_index].items[card_index].show_warnings, compareObjectsEmptiness); const card_counted_resources = useShopStore(state => state.crates[crate_index].items[card_index].counted_resources, compareObjectsEmptiness); - const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card); + const highlighted = useShopStore((state) => + !state.highlighted.horizontal && + state.crates[crate_index].id === state.highlighted.crate && + card_index === state.highlighted.card); const warnings_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled); const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options); const crate_id = useShopStore((state) => state.crates[crate_index].id); @@ -58,7 +61,7 @@ export function ProductCartItem({card_index, crate_index, first, last}) { true ) }} - onMouseEnter={() => setHighlight(crate_id, card_index)} + onMouseEnter={() => setHighlight(crate_id, card_index, false)} onMouseLeave={removeHighlight} > @@ -73,8 +76,6 @@ export function ProductCartItem({card_index, crate_index, first, last}) { )}
diff --git a/static/js/shop/ProductCartItemHorizontal.jsx b/static/js/shop/ProductCartItemHorizontal.jsx new file mode 100644 index 0000000..5bc7e46 --- /dev/null +++ b/static/js/shop/ProductCartItemHorizontal.jsx @@ -0,0 +1,98 @@ +import React from 'react' +import {Draggable} from "@hello-pangea/dnd"; +import {compareObjectsEmptiness, productStyle} from "./utils"; +import {Resources} from "./Resources"; +import {CardWarnings} from "./CardWarnings"; +import {useShopStore} from "./shop_store"; +import {OptionsDialogWrapper} from "./OptionsWrapper"; + +// #!render_count +import {useRenderCount} from "@uidotdev/usehooks"; + +/** + * Component that renders a product. + * Used in the crate + */ +export function ProductCartItemHorizontal({card_index, crate_index}) { + // #!render_count + const renderCount = useRenderCount(); + + const card = useShopStore((state) => state.crates[crate_index].h_items[card_index], + (a, b) => a.id === b.id); + + const highlighted = useShopStore((state) => + !!state.highlighted.horizontal && + state.crates[crate_index].id === state.highlighted.crate && + card_index === state.highlighted.card + ); + const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options); + const crate_id = useShopStore((state) => state.crates[crate_index].id); + const setHighlight = useShopStore((state) => state.highlightCard); + const removeHighlight = useShopStore((state) => state.highlightReset); + const onCardRemove = useShopStore((state) => state.deleteCard); + + // #!render_count + console.log("ProductCartItem renders: ", renderCount) + + + const options = use_options && card && card[use_options] && card[use_options].length > 0; + + return ( + + + {(provided, snapshot) => ( +
setHighlight(crate_id, card_index, true)} + onMouseLeave={removeHighlight} + > + + {/* warning container */} + +
+ {options && ( + + )} +
+ +
setHighlight(crate_id, card_index, true)} + onClick={() => setHighlight(crate_id, card_index, true)} + > + + {`${card.name_number} ${card.name} ${card.name_codename}`} +
+ + {/* remove container */} +
onCardRemove(crate_id, card_index, true)}> + + rm + +
+ +
+ )} + +
+ ); +} diff --git a/static/js/shop/ProductItem.jsx b/static/js/shop/ProductItem.jsx index 9b429d6..9333622 100644 --- a/static/js/shop/ProductItem.jsx +++ b/static/js/shop/ProductItem.jsx @@ -7,6 +7,8 @@ import {useShopStore} from "./shop_store"; import {useRenderCount} from "@uidotdev/usehooks"; +const DNDIcon = "/images/shop/icon-drag-and-drop.svg"; + function DatasheetLink({datasheet_file, datasheet_name}) { return datasheet_file && datasheet_name && (
@@ -76,11 +78,12 @@ export function ProductItem({card_index}) { snapshot, true, // hack: remove weird animation after a drop )} - src={card.image}/> + className={card.image ? "" : "default-icon"} + src={card.image || DNDIcon}/> {/* Allows to simulate a clone */} {snapshot.isDragging && ( - + )} )} diff --git a/static/js/shop/SummaryCrate.jsx b/static/js/shop/SummaryCrate.jsx index caa2abf..09b4edc 100644 --- a/static/js/shop/SummaryCrate.jsx +++ b/static/js/shop/SummaryCrate.jsx @@ -15,6 +15,7 @@ export function SummaryCrate({crate_index}) { const crate_id = useShopStore((state) => state.crates[crate_index].id); const crate_len = useShopStore((state) => state.crates[crate_index].items.length); + const crate_h_len = useShopStore((state) => state.crates[crate_index].h_items.length); // #!render_count console.log("SummaryCrate renders: ", renderCount) @@ -28,6 +29,14 @@ export function SummaryCrate({crate_index}) { )} + {range(0, crate_h_len).map((index, _i) => + + )} + ) diff --git a/static/js/shop/SummaryCrateCard.jsx b/static/js/shop/SummaryCrateCard.jsx index 00c5658..36e0ac9 100644 --- a/static/js/shop/SummaryCrateCard.jsx +++ b/static/js/shop/SummaryCrateCard.jsx @@ -1,28 +1,39 @@ import {compareObjectsEmptiness, formatMoney} from "./utils"; import {WarningIndicator} from "./CardWarnings"; import React from "react"; -import {useShopStore} from "./shop_store"; +import {useShopStore, whichItems} from "./shop_store"; import {OptionsSummaryWrapper} from "./OptionsWrapper"; // #!render_count import {useRenderCount} from "@uidotdev/usehooks"; -export function SummaryCrateCard({crate_index, card_index}) { +export function SummaryCrateCard({crate_index, card_index, horizontal}) { // #!render_count const renderCount = useRenderCount(); + const whichH = whichItems(horizontal); + const currency = useShopStore((state) => state.currency); const deleteCard = useShopStore((state) => state.deleteCard); const setHighlight = useShopStore((state) => state.highlightCard); const resetHighlight = useShopStore((state) => state.highlightReset); - const highlighted = useShopStore((state) => state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card); + const highlighted = useShopStore((state) => + state.highlighted.horizontal === horizontal && + state.crates[crate_index].id === state.highlighted.crate && + card_index === state.highlighted.card); const crate_id = useShopStore((state) => state.crates[crate_index].id); - const card = useShopStore((state) => state.crates[crate_index].items[card_index], + const card = useShopStore((state) => + state.crates[crate_index][whichH][card_index], (a, b) => a.id === b.id); - const card_show_warnings = useShopStore(state => state.crates[crate_index].items[card_index].show_warnings, compareObjectsEmptiness); - const card_options_data = useShopStore(state => state.crates[crate_index].items[card_index].options_data, compareObjectsEmptiness); + // additional hooks for updating warning and options + const card_show_warnings = useShopStore(state => + state.crates[crate_index][whichH][card_index].show_warnings, + compareObjectsEmptiness); + const card_options_data = useShopStore(state => + state.crates[crate_index][whichH][card_index].options_data, + compareObjectsEmptiness); const warnings_disabled = useShopStore((state) => !!state.crateParams(state.crates[crate_index].crate_mode).warnings_disabled); const use_options = useShopStore((state) => state.crateParams(state.crates[crate_index].crate_mode).options); @@ -38,8 +49,8 @@ export function SummaryCrateCard({crate_index, card_index}) { setHighlight(crate_id, card_index)} - onMouseEnter={() => setHighlight(crate_id, card_index)} + onClick={() => setHighlight(crate_id, card_index, horizontal)} + onMouseEnter={() => setHighlight(crate_id, card_index, horizontal)} onMouseLeave={() => resetHighlight()}>
{`${card.name_number} ${card.name} ${card.name_codename}`}
@@ -49,7 +60,7 @@ export function SummaryCrateCard({crate_index, card_index}) {
{`${currency} ${formatMoney(card.price)}`} - @@ -64,7 +75,10 @@ export function SummaryCrateCard({crate_index, card_index}) { }}>  ))} {((options && options_data) ? ( - + ) : ( ({ + ...pn_to_card(card.pn), + id: uuidv4(), + options_data: card.options || {} + }))), warnings: [], occupiedHP: 0, }))); @@ -73,6 +81,10 @@ export function CratesToJSON(crates) { pn: card.name_number, options: (card.options_data && card[crateParams(crate.crate_mode).options]) ? FilterOptions(card[crateParams(crate.crate_mode).options], card.options_data) : null }))), + h_items: Array.from(crate.h_items.map((card, _) => ({ + pn: card.name_number, + options: (card.options_data && card[crateParams(crate.crate_mode).options]) ? FilterOptions(card[crateParams(crate.crate_mode).options], card.options_data) : null + }))), type: crate.crate_mode, options: FilterOptions(crateOptions, crate.options_data) }))), diff --git a/static/js/shop/shop_store.js b/static/js/shop/shop_store.js index 3bddac2..c9900b4 100644 --- a/static/js/shop/shop_store.js +++ b/static/js/shop/shop_store.js @@ -13,6 +13,8 @@ import {ProcessOptionsToData} from "./options/Options"; import {DomainedRFQMessages} from "./Domained"; +export const HORIZONTAL_CART_MARKER = "_h"; + const cards_to_pn_map = (cards) => { let result = {}; Object.entries(cards).forEach(([key, card], _i) => { result[card.name_number] = key}) @@ -23,6 +25,12 @@ const toArray = (arg) => { return Array.isArray(arg) ? arg : [arg]; }; +const unwrapCrateId = (crate_id= "") => { + return crate_id.endsWith(HORIZONTAL_CART_MARKER) ? [true, crate_id.substring(0, crate_id.length - HORIZONTAL_CART_MARKER.length)] : [false, crate_id] +} + +export const whichItems = (horizontal = false) => horizontal ? "h_items" : "items" + const useCatalog = ((set, get) => ({ cards: shared_data.items, groups: shared_data.columns.catalog, @@ -37,21 +45,24 @@ const useCatalog = ((set, get) => ({ const useOptionsNotification = ((set, get) => ({ notificationCrateId: null, notificationCardIndex: null, + notificationHorizontal: false, notificationTimer: null, - _showNotification: (crate_id, card_index) => set(state => ({ + _showNotification: (crate_id, card_index, horizontal) => set(state => ({ notificationCrateId: crate_id, notificationCardIndex: card_index, + notificationHorizontal: horizontal, notificationTimer: setTimeout(() => { state.hideNotification() }, 5000) })), - showNotification: (crate_id, card_index) => { + showNotification: (crate_id, card_index, horizontal) => { get().hideNotification() - setTimeout(() => get()._showNotification(crate_id, card_index), 100); + setTimeout(() => get()._showNotification(crate_id, card_index, horizontal), 100); }, hideNotification: () => set(state => ({ notificationCrateId: null, notificationCardIndex: null, + notificationHorizontal: false, notificationTimer: (state.notificationTimer && clearTimeout(state.notificationTimer)) || null, })) })); @@ -215,7 +226,7 @@ const useImportJSON = ((set, get) => ({ get().fillExtCrateData(crate.id); }); get()._updateTotalOrderPrice(); - get().showNotification(get().active_crate, null); + get().showNotification(get().active_crate, null, false); }, updateImportDescription: (new_description) => set(state => ({ importValue: { @@ -332,15 +343,17 @@ const useSubmitForm = ((set, get) => ({ const useHighlighted = ((set, get) => ({ highlighted: { crate: "", - card: 0 + card: 0, + horizontal: false }, highlightedTimer: null, // #!if disable_card_highlight === false - highlightCard: (crate_id, index) => set(state => ({ + highlightCard: (crate_id, index, horizontal) => set(state => ({ highlighted: { crate: crate_id, - card: index + card: index, + horizontal: horizontal }, highlightedTimer: (!!state.highlightedTimer ? clearTimeout(state.highlightedTimer) : null) || (state.isTouch && setTimeout(() => { get().highlightReset() @@ -349,7 +362,8 @@ const useHighlighted = ((set, get) => ({ highlightReset: () => set(state => ({ highlighted: { crate: "", - card: 0 + card: 0, + horizontal: false }, highlightedTimer: !!state.highlightedTimer ? clearTimeout(state.highlightedTimer) : null })), @@ -388,17 +402,18 @@ const useCart = ((set, get) => ({ }) })), setActiveCrate: (id) => set(state => ({active_crate: id})), - _addCardFromCatalog: (crate_to, index_from, index_to) => set(state => { + _addCardFromCatalog: (crate_to, index_from, index_to, horizontal) => set(state => { + const whichH = whichItems(horizontal) const take_from = toArray(index_from).map((item, _i) => (state.cards_list[item])); const dest = crate_to || state.active_crate; if (!dest) return {}; return { crates: state.crates.map((crate, _i) => { if (dest === crate.id) { - index_to = index_to != null ? index_to : crate.items.length; + index_to = index_to != null ? index_to : crate[whichH].length; return { ...crate, - items: crate.items.toSpliced(index_to, 0, ...take_from.map((card_name, _) => { + [whichH]: crate[whichH].toSpliced(index_to, 0, ...take_from.map((card_name, _) => { return {...state.cards[card_name], id: uuidv4()} })) } @@ -406,39 +421,41 @@ 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 ).items[index_from]; + _moveCard: (crate_from, index_from, crate_to, index_to, horizontal) => set(state => { + const whichH = whichItems(horizontal) + const the_card = state.crates.find((crate, _) => crate_from === crate.id )[whichH][index_from]; return { crates: state.crates.map((crate, _i) => { if (crate_to === crate_from && crate_to === crate.id) { - let items_copy = Array.from(crate.items); + let items_copy = Array.from(crate[whichH]); let item = items_copy.splice(index_from, 1)[0] items_copy.splice(index_to, 0, item).filter((item, _) => !!item) return { ...crate, - items: items_copy + [whichH]: items_copy } } else if (crate_to === crate.id) { return { ...crate, - items: crate.items.toSpliced(index_to, 0, the_card) + [whichH]: crate[whichH].toSpliced(index_to, 0, the_card) } } else if (crate_from === crate.id) { return { ...crate, - items: crate.items.toSpliced(index_to, 1) + [whichH]: crate[whichH].toSpliced(index_to, 1) } } else return crate; }) } }), - _deleteCard: (crate_id, index) => set(state => ({ + _deleteCard: (crate_id, index, horizontal) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { + const whichH = whichItems(horizontal) return { ...crate, - items: crate.items.toSpliced(index, 1) + [whichH]: crate[whichH].toSpliced(index, 1) } } else return crate; @@ -449,7 +466,8 @@ const useCart = ((set, get) => ({ if (id === crate.id) { return { ...crate, - items: [] + items: [], + h_items: [] } } else return crate; @@ -458,10 +476,11 @@ const useCart = ((set, get) => ({ clearAll: () => set(state => ({ crates: state._defaultCrates })), - _updateOptions: (crate_id, index, new_options) => set(state => ({ + _updateOptions: (crate_id, index, new_options, horizontal) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { - let itemsCopy = Array.from(crate.items); + const whichH = whichItems(horizontal) + let itemsCopy = Array.from(crate[whichH]); itemsCopy[index] = { ...itemsCopy[index], options_data: { @@ -470,7 +489,7 @@ const useCart = ((set, get) => ({ }}; return { ...crate, - items: itemsCopy + [whichH]: itemsCopy } } else return crate; @@ -480,7 +499,7 @@ const useCart = ((set, get) => ({ fillWarnings: (crate_id) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { - //console.log("--- CHECK ALERTS ---") + // Warnings for horizontal items are not available let itemsCopy = Array.from(crate.items); const disabled = !!get().crateParams(crate.crate_mode).warnings_disabled; itemsCopy = FillResources(itemsCopy, disabled); @@ -497,11 +516,13 @@ const useCart = ((set, get) => ({ }) })), - fillExtData: (crate_id) => set(state => ({ + fillExtData: (crate_id, horizontal) => set(state => ({ crates: state.crates.map((crate, _i) => { + // horizontal items do not interact with each other for now if (crate_id === crate.id) { + const whichH = whichItems(horizontal) const options_name = state.crateParams(crate.crate_mode).options; - let itemsCopy = Array.from(crate.items); + let itemsCopy = Array.from(crate[whichH]); itemsCopy = itemsCopy.map((item, index) => { if (!item[options_name]) return item; @@ -511,7 +532,7 @@ const useCart = ((set, get) => ({ }); return { ...crate, - items: Array.from(itemsCopy) + [whichH]: Array.from(itemsCopy) } } else return crate; @@ -524,7 +545,7 @@ const useCart = ((set, get) => ({ sum += get().crate_modes[crate.crate_mode].price; const crate_options = ProcessOptionsToData({options: get().crate_prices, data: crate.options_data || {}}); sum += crate_options ? crate_options.reduce((accumulator, currentValue) => accumulator+currentValue.price, 0) : 0; - crate.items.forEach((item, _) => { + crate.items.concat(crate.h_items).forEach((item, _) => { sum += item.price; }); }); @@ -539,6 +560,7 @@ const useCart = ((set, get) => ({ const crate_id = "crate" + get().crates.length; get()._newCrate(crate_id) get().fillExtData(crate_id); + get().fillExtData(crate_id, true); get().fillExtCrateData(crate_id); get().fillOrderExtData(); get().fillWarnings(crate_id); @@ -548,6 +570,7 @@ const useCart = ((set, get) => ({ setCrateMode: (id, mode) => { get()._setCrateMode(id, mode) get().fillExtData(id); + get().fillExtData(id, horizontal); get().fillExtCrateData(id); get().fillOrderExtData(); get().fillWarnings(id); @@ -563,15 +586,17 @@ const useCart = ((set, get) => ({ addCardFromCatalog: (crate_to, index_from, index_to, just_mounted) => { const isCrateless = toArray(index_from).some(value => get().getCardDescription(value).crateless === true); - const dest = isCrateless ? "spare" : crate_to || get().active_crate; + const [isHorizontal, crateTo] = crate_to ? unwrapCrateId(crate_to) : [false, crate_to]; + const isHorizontalOnly = toArray(index_from).some(value => !get().getCardDescription(value).image); + const dest = isCrateless ? "spare" : crateTo || get().active_crate; if (!dest) { console.warn("No destination"); get().noDestinationWarning(); return {}; } - get().showNotification(dest, index_to); - get()._addCardFromCatalog(dest, index_from, index_to) - get().fillExtData(dest); + get().showNotification(dest, index_to, isHorizontalOnly); + get()._addCardFromCatalog(dest, index_from, index_to, isHorizontalOnly) + get().fillExtData(dest, isHorizontalOnly); get().fillWarnings(dest); get().setActiveCrate(dest); get()._updateTotalOrderPrice(); @@ -581,22 +606,25 @@ const useCart = ((set, get) => ({ }, moveCard: (crate_from, index_from, crate_to, index_to) => { - get()._moveCard(crate_from, index_from, crate_to, index_to); - get().fillExtData(crate_to); - get().fillWarnings(crate_to); - get().setActiveCrate(crate_to); + const [isHorizontal, crateFrom] = unwrapCrateId(crate_from) + const [_, crateTo] = unwrapCrateId(crate_to) + get()._moveCard(crateFrom, index_from, crateTo, index_to, isHorizontal); + get().fillExtData(crateTo, isHorizontal); + get().fillWarnings(crateTo); + get().setActiveCrate(crateTo); get()._updateTotalOrderPrice(); - if (crate_from !== crate_to) { - get().fillExtData(crate_from); - get().fillWarnings(crate_from); + if (crateFrom !== crate_to) { + get().fillExtData(crateFrom, isHorizontal); + get().fillWarnings(crateFrom); } }, - deleteCard: (crate_id, index) => { - get()._deleteCard(crate_id, index); - get().fillExtData(crate_id); - get().fillWarnings(crate_id); + deleteCard: (crate_id, index, horizontal) => { + const [isHorizontal, crateId] = horizontal ? [horizontal, crate_id] : unwrapCrateId(crate_id); + get()._deleteCard(crateId, index, isHorizontal); + get().fillExtData(crateId, isHorizontal); + get().fillWarnings(crateId); get()._updateTotalOrderPrice(); - if (crate_id === get().highlighted.crate && index === get().highlighted.card) get().highlightReset() + if (crateId === get().highlighted.crate && index === get().highlighted.card) get().highlightReset() }, clearCrate: (id) => { get()._clearCrate(id); @@ -604,16 +632,19 @@ const useCart = ((set, get) => ({ get()._updateTotalOrderPrice(); }, - updateOptions: (crate_id, index, new_options) => { - get()._updateOptions(crate_id, index, new_options); - get().fillExtData(crate_id); - get().fillWarnings(crate_id); + updateOptions: (crate_id, index, new_options, horizontal) => { + get()._updateOptions(crate_id, index, new_options, horizontal); + get().fillExtData(crate_id, horizontal); + if (!horizontal) { + get().fillWarnings(crate_id); + } }, initExtData: () => { get().fillOrderExtData(); get().crates.forEach((crate, _i) => { - get().fillExtData(crate.id); + get().fillExtData(crate.id, true); + get().fillExtData(crate.id, false); get().fillExtCrateData(crate.id); }) get()._updateTotalOrderPrice(); diff --git a/static/js/shop_data.js b/static/js/shop_data.js index ae86909..a63f302 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -1449,7 +1449,7 @@ const shop_data = { name_number: 'AFWS', name_codename: '', price: 800, - image: '/images/shop/graphic-03_AFWS.svg', + image: null, specs: [ "Artiq Firmware Service for one variant for one year.", "Includes support at helpdesk.", @@ -1530,6 +1530,7 @@ const shop_data = { crate_mode: "rack", fan_tray: false, items: [], + h_items: [], warnings: [], occupiedHP: 0, }, @@ -1538,6 +1539,7 @@ const shop_data = { name: "Spare items", crate_mode: "no_crate", items: [], + h_items: [], warnings: [], occupiedHP: 0, }