diff --git a/static/js/shop/Backlog.jsx b/static/js/shop/Backlog.jsx index c207e6a..39afc9d 100644 --- a/static/js/shop/Backlog.jsx +++ b/static/js/shop/Backlog.jsx @@ -9,17 +9,13 @@ import {useShopStore} from "./shop_store"; */ export function Backlog() { const { - currency, data, items, - onClickAddItem, onClickToggleMobileSideMenu, isMobile, } = useShopStore(state=> ({ - currency: state.currency, data: state.groups, items: state.cards, - onClickAddItem: state.addCardFromBacklog, onClickToggleMobileSideMenu: state.switchSideMenu, isMobile: state.isMobile })); @@ -46,20 +42,7 @@ export function Backlog() { {group.items.map(item => { item_index++; return ( - + ) })} diff --git a/static/js/shop/Cart.jsx b/static/js/shop/Cart.jsx index 965b9ce..ecf1641 100644 --- a/static/js/shop/Cart.jsx +++ b/static/js/shop/Cart.jsx @@ -4,29 +4,23 @@ import {cartStyle} from "./utils"; import {ProductCartItem} from "./ProductCartItem.jsx"; import {FakePlaceholder} from "./FakePlaceholder.jsx"; import {FillExtData} from "./options/utils"; -import {CountResources, crate_type_to_hp, hp_to_slots, resource_counters} from "./count_resources"; +import {hp_to_slots, resource_counters} from "./count_resources"; import {useShopStore} from "./shop_store"; -import {TriggerCardWarnings} from "./warnings"; /** * Component that displays a list of */ export function Cart({crate_index}) { - // isMobile, isTouch, crate, onToggleOverlayRemove, onClickRemoveItem, onCardUpdate, onClickItem - const {crate} = useShopStore(state => ({ - crate: state.crates[crate_index] + const {crate, crateParams} = useShopStore(state => ({ + crate: state.crates[crate_index], + crateParams: state.crateParams })); - 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); + const nbrOccupied = hp_to_slots(crate.occupiedHP); + const nbrSlots = hp_to_slots(crateParams(crate.crate_mode).hp); const products = crate.items.map((item, index) => { const ext_data = FillExtData(crate.items, index); - const resources = CountResources(crate.items, index); - const warnings = TriggerCardWarnings(crate.items, index, resources); return ( = nbrSlots} - resources={resources} - warnings={warnings} key={item.id}/> ); }); diff --git a/static/js/shop/Crate.jsx b/static/js/shop/Crate.jsx index 1115cce..9f23ca4 100644 --- a/static/js/shop/Crate.jsx +++ b/static/js/shop/Crate.jsx @@ -34,9 +34,7 @@ export function Crate({crate_index}) { - {1 || (rules && rules.length > 0) && ( - - )} + ); diff --git a/static/js/shop/CrateMode.jsx b/static/js/shop/CrateMode.jsx index c8bcd91..ac207ee 100644 --- a/static/js/shop/CrateMode.jsx +++ b/static/js/shop/CrateMode.jsx @@ -8,7 +8,7 @@ export function CrateMode({crate_index}) { const {modes_order, crate_modes, crate, setMode} = useShopStore(state => ({ modes_order: state.modes_order, crate_modes: state.crate_modes, - crate: state.crates[crate_index].crate_mode, + crate: state.crates[crate_index], setMode: state.setCrateMode })) return ( diff --git a/static/js/shop/CrateWarnings.jsx b/static/js/shop/CrateWarnings.jsx index 4f80d3a..8c26631 100644 --- a/static/js/shop/CrateWarnings.jsx +++ b/static/js/shop/CrateWarnings.jsx @@ -1,16 +1,16 @@ import React from "react"; -import {TriggerCrateWarnings} from "./warnings"; +import {LevelUI} from "./warnings"; import {useShopStore} from "./shop_store"; export function CrateWarnings({crate_index}) { const crate = useShopStore(state => (state.crates[crate_index])) - const crate_warnings = TriggerCrateWarnings(crate); + const crate_warnings = crate.warnings; // TODO UI/colors return (
{crate_warnings.map((rule, index) => ( -

- {rule.name}: {rule.message} +

+ {rule.message}

))}
diff --git a/static/js/shop/OrderPanel.jsx b/static/js/shop/OrderPanel.jsx index ada7a91..ea95b4c 100644 --- a/static/js/shop/OrderPanel.jsx +++ b/static/js/shop/OrderPanel.jsx @@ -40,7 +40,7 @@ export function OrderPanel({title, description}) {
- +
diff --git a/static/js/shop/OrderSummary.jsx b/static/js/shop/OrderSummary.jsx index aed8ccc..846d20e 100644 --- a/static/js/shop/OrderSummary.jsx +++ b/static/js/shop/OrderSummary.jsx @@ -1,190 +1,149 @@ -import React, {PureComponent} from 'react'; -import PropTypes from "prop-types"; +import React from 'react'; import {SummaryPopup} from "./options/SummaryPopup.jsx"; import {formatMoney} from "./utils"; import {WarningIndicator} from "./CardWarnings.jsx"; -import {total_order_price} from "./count_resources"; -import {data as shared_data} from "./utils"; +import {useShopStore} from "./shop_store"; /** * Components that displays the list of card that are used in the crate. * It is a summary of purchase */ -export class OrderSummary extends PureComponent { +export function OrderSummary() { - static get propTypes() { - return { - currency: PropTypes.string, - crates: PropTypes.object, - onDeleteItem: PropTypes.func, - onDeleteAllItems: PropTypes.func, - onMouseEnterItem: PropTypes.func, - onMouseLeaveItem: PropTypes.func, - onClickSelectItem: PropTypes.func, - }; - } + const { + currency, + crates, + total_price, + crateParams, + deleteCard, + setHighlight, + resetHighlight, + highlighted, + clearCrate, + clearAll + } = useShopStore(state =>({ + currency: state.currency, + crates: state.crates, + total_price: state.totalOrderPrice(), + crateParams: state.crateParams, + deleteCard: state.deleteCard, + setHighlight: state.highlightCard, + resetHighlight: state.highlightReset, + highlighted: state.highlighted, + clearAll: state.clearAll, + clearCrate: state.clearCrate + })); - constructor(props) { - super(props); - this.handleOnDeleteItem = this.handleOnDeleteItem.bind(this); - this.handleOnDeleteAllItems = this.handleOnDeleteAllItems.bind(this); - this.handleOnMouseEnterItem = this.handleOnMouseEnterItem.bind(this); - this.handleOnMouseLeaveItem = this.handleOnMouseLeaveItem.bind(this); - this.handleOnClickSelectItem = this.handleOnClickSelectItem.bind(this); - } + return ( +
- handleOnDeleteItem(index, e) { - if (this.props.onDeleteItem) { - this.props.onDeleteItem(index); - } - e.preventDefault(); - } + - handleOnDeleteAllItems(e) { - if (this.props.onDeleteAllItems) { - this.props.onDeleteAllItems(); - } - e.preventDefault(); - } + + + + + - handleOnMouseLeaveItem(e) { - if (this.props.onMouseLeaveItem) { - this.props.onMouseLeaveItem(); - } - e.preventDefault(); - } + {crates.map((crate, _i) => { + let crate_type = crateParams(crate.crate_mode); + return ( + + + + + + {crate.items.map((item, index) => { + const options = item && item.options; + const options_data = item && item.options_data; + const warnings = item && item.show_warnings; + const selected = crate.id === highlighted.crate && index === highlighted.card; - const total_price = total_order_price(crates); - - return ( -
- -
+ Remove all cards - handleOnMouseEnterItem(id, e) { - if (this.props.onMouseEnterItem) { - this.props.onMouseEnterItem(id); - } - e.preventDefault(); - } + +
{crate_type.name} +
+ {`${currency} ${formatMoney(crate_type.price)}`} - handleOnClickSelectItem(index, e) { - if (e.target.tagName !== 'IMG') { - if (this.props.onClickSelectItem) { - this.props.onClickSelectItem(index); - } - } - return e.preventDefault(); - } + +
- render() { - const { - currency, - crates - } = this.props; +   +
- - - - - - - - {Object.entries(crates).map(([crate_id, crate], _i) => { - let crate_type = shared_data.crateModes[crate.crate_type]; - return ( - - - - setHighlight(crate.id, index)} + onMouseEnter={() => setHighlight(crate.id, index)} + onMouseLeave={() => resetHighlight()}> + - - {crate.items.map((item, index) => { - let options = item && item.options; - let options_data = item && item.options_data; - const warnings = item && item.show_warnings; - return ( - + + ); + })} + + ) + })} -
- {(warnings && warnings.length > 0 ? ( - - ) : ( -   - ))} - {((options && options_data) ? ( - - ) : ( -   - ))} -
- - - ); - })} - - )})} + + + + - - - + + -   - - - +
- Remove all cards - - -
{crate_type.name} -
- {`${currency} ${formatMoney(crate_type.price)}`} - - -
- -   + return (
+
{`${item.name_number} ${item.name} ${item.name_codename}`}
-
{`${item.name_number} ${item.name} ${item.name_codename}`}
-
+
+ {`${currency} ${formatMoney(item.price)}`} -
-
- {`${currency} ${formatMoney(item.price)}`} + - +
+ {(warnings && warnings.length > 0 ? ( + + ) : ( +   + ))} + {((options && options_data) ? ( + + ) : ( +   + ))} +
+
+
Price estimate +
+ {currency} {formatMoney(total_price)} + +
-
Price estimate -
- ${currency} ${formatMoney(total_price)} - -
+   +
- - -
- ); - } + + ); } \ No newline at end of file diff --git a/static/js/shop/ProductCartItem.jsx b/static/js/shop/ProductCartItem.jsx index 429e9ca..f817aff 100644 --- a/static/js/shop/ProductCartItem.jsx +++ b/static/js/shop/ProductCartItem.jsx @@ -10,7 +10,7 @@ import {useShopStore} from "./shop_store"; * Component that renders a product. * Used in the crate */ -export function ProductCartItem({card_index, crate_index, ext_data, first, last, resources, warnings}) { +export function ProductCartItem({card_index, crate_index, ext_data, first, last}) { const {card, crate, highlighted, setHighlight, removeHighlight, onCardUpdate, onCardRemove} = useShopStore(state => ({ card: state.crates[crate_index].items[card_index], highlighted: state.crates[crate_index].id === state.highlighted.crate && card_index === state.highlighted.card, @@ -22,7 +22,8 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last, })) let options, options_data; - //const warnings = data && data.show_warnings; + const warnings = card && card.show_warnings; + const resources = card && card.counted_resources; if (card && card.options) { options = card.options; @@ -94,7 +95,7 @@ export function ProductCartItem({card_index, crate_index, ext_data, first, last, + src={card.image}/> {/* remove container */} diff --git a/static/js/shop/ProductItem.jsx b/static/js/shop/ProductItem.jsx index 532ce36..bbf7164 100644 --- a/static/js/shop/ProductItem.jsx +++ b/static/js/shop/ProductItem.jsx @@ -1,120 +1,84 @@ -import React, {PureComponent} from 'react'; -import PropTypes from "prop-types"; +import React from 'react'; import {Draggable} from "@hello-pangea/dnd"; import {formatMoney, productStyle} from "./utils"; +import {useShopStore} from "./shop_store"; /** * Component that renders a product. * Used in the aside (e.g backlog of product) */ -export class ProductItem extends PureComponent { +export function ProductItem({card_index}) { + const {card, currency, onAddCard} = useShopStore(state => ({ + card: state.getCardDescription(card_index), + currency: state.currency, + onAddCard: state.addCardFromBacklog + })); - static get propTypes() { - return { - id: PropTypes.string.isRequired, - index: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - name_codename: PropTypes.string, - price: PropTypes.number.isRequired, - currency: PropTypes.string.isRequired, - image: PropTypes.string.isRequired, - specs: PropTypes.array, - datasheet_file: PropTypes.string, - datasheet_name: PropTypes.string, - onClickAddItem: PropTypes.func, - }; - } - constructor(props) { - super(props); - this.handleOnClickAddItem = this.handleOnClickAddItem.bind(this); - } + const render_specs = (card.specs && card.specs.length > 0 && ( +
    + {card.specs.map((spec, index) => +
  • {spec}
  • + )} +
+ )); - handleOnClickAddItem(id, tap, e) { - if (this.props.onClickAddItem) { - this.props.onClickAddItem(id, tap); - } - e.preventDefault(); - } + const render_datasheet_link = (card.datasheet_file && card.datasheet_name && ( + + )); - render() { - const { - id, - index, - name, - name_codename, - price, - currency, - image, - specs, - datasheet_file, - datasheet_name, - } = this.props; + return ( +
- const render_specs = (specs && specs.length > 0 && ( -
    - {specs.map((spec, index) => -
  • {spec}
  • - )} -
- )); +
+

{card.name}

+ {card.name_codename ? ( +

{card.name_codename}

+ ) : null} - const render_datasheet_link = (datasheet_file && datasheet_name && ( -
- - - {datasheet_name} - +
{`${currency} ${formatMoney(card.price)}`}
+ + {render_specs} + + {render_datasheet_link}
- )); - return ( -
+
-
-

{name}

- {name_codename ? ( -

{name_codename}

- ) : null } + -
{`${currency} ${formatMoney(price)}`}
- - {render_specs} - - {render_datasheet_link} -
- -
- - - - - {(provided, snapshot) => ( - - - - {/* Allows to simulate a clone */} - {snapshot.isDragging && ( - + + {(provided, snapshot) => ( + + - )} - + src={card.image}/> -
+ {/* Allows to simulate a clone */} + {snapshot.isDragging && ( + + )} + + )} + + +
+ +
+ ); -
- ); - } } \ No newline at end of file diff --git a/static/js/shop/Shop.jsx b/static/js/shop/Shop.jsx index d3c12f4..10e02f2 100644 --- a/static/js/shop/Shop.jsx +++ b/static/js/shop/Shop.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import {DragDropContext} from "@hello-pangea/dnd"; @@ -12,28 +12,13 @@ import {useShopStore} from "./shop_store"; */ export function Shop() { - const {addCardFromBacklog, moveCard, deleteCard} = useShopStore(state => ({ + const {addCardFromBacklog, moveCard, deleteCard, cardIndexById} = useShopStore(state => ({ addCardFromBacklog: state.addCardFromBacklog, moveCard: state.moveCard, - deleteCard: state.deleteCard + deleteCard: state.deleteCard, + cardIndexById: state.cardIndexById })); 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") @@ -42,6 +27,10 @@ export function Shop() { moveCard(drop_result.source.droppableId, drop_result.source.index, drop_result.destination.droppableId, drop_result.destination.index) } + useEffect(() => { + addCardFromBacklog(null, [cardIndexById("kasli"), cardIndexById("eem_pwr_mod")], -1); + }, []); + return ( ({ - cards: data.items, - groups: data.columns.backlog, + cards: shared_data.items, + groups: shared_data.columns.backlog, cards_list: itemsUnfoldedList, - currency: data.currency + currency: shared_data.currency, + getCardDescription: index => get().cards[get().cards_list[index]], + cardIndexById: card_id => get().cards_list.findIndex((element) => (card_id === element)) })); const useCrateModes = ((set, get) => ({ - crate_modes: data.crateModes, - modes_order: data.crateModeOrder + crate_modes: shared_data.crateModes, + modes_order: shared_data.crateModeOrder, + crateParams: mode => get().crate_modes[mode], })); const useLayout = ((set, get) => ({ @@ -47,25 +50,41 @@ const useSubmitForm = ((set, get) => ({ isProcessingComplete: true, })); -const useCart = ((set, get) => ({ - crates: data.columns.crates, - active_crate: "crate0", +const useHighlighted = ((set, get) => ({ highlighted: { crate: "", card: 0 }, + highlightCard: (crate_id, index) => set(state => ({ + highlighted: { + crate: crate_id, + card: index + } + })), + highlightReset: () => set(state => ({ + highlighted: { + crate: "", + card: 0 + } + })), +})); - newCrate: () => set((state) => ({crates: state.crates.concat({ - id: "crate"+state.crates.length, +const useCart = ((set, get) => ({ + crates: shared_data.columns.crates, + active_crate: "crate0", + + _newCrate: (crate_id) => set((state) => ({crates: state.crates.concat({ + id: crate_id || "crate" + state.crates.length, crate_mode: "rack", items: [], - warnings: [] + warnings: [], + occupiedHP: 0 })})), delCrate: (id) => set(state => ({ crates: state.crates.filter((crate => crate.id !== id)) })), - setCrateMode: (id, mode) => set(state => ({ + _setCrateMode: (id, mode) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate.id === id) { return { @@ -76,12 +95,13 @@ const useCart = ((set, get) => ({ }) })), setActiveCrate: (id) => set(state => ({active_crate: id})), - addCardFromBacklog: (crate_to, index_from, index_to) => set(state => { + _addCardFromBacklog: (crate_to, index_from, index_to) => set(state => { const take_from = (true_type_of(index_from) === "array" ? index_from : [index_from]).map((item, _i) => (state.cards_list[item])); const dest = crate_to || state.active_crate; return { crates: state.crates.map((crate, _i) => { if (dest === crate.id) { + index_to = index_to != null ? index_to : crate.items.length; return { ...crate, items: crate.items.toSpliced(index_to, 0, ...take_from.map((card_name, _) => { @@ -92,7 +112,8 @@ const useCart = ((set, get) => ({ }) } }), - moveCard: (crate_from, index_from, crate_to, index_to) => set(state => { + _moveCard: (crate_from, index_from, crate_to, index_to) => set(state => { + console.log(crate_from, index_from, crate_to, index_to) const the_card = state.crates.find((crate, _) => crate_from === crate.id ).items[index_from]; return { crates: state.crates.map((crate, _i) => { @@ -102,7 +123,7 @@ const useCart = ((set, get) => ({ delete items_copy[index_from]; return { ...crate, - items: items_copy.toSpliced(index_to, 0, the_card).filter((item, _) => !!item) + items: items_copy.toSpliced(index_to+1, 0, the_card).filter((item, _) => !!item) } } else if (crate_to === crate.id) { return { @@ -119,18 +140,18 @@ const useCart = ((set, get) => ({ }) } }), - deleteCard: (crate, index) => set(state => ({ + _deleteCard: (crate_id, index) => set(state => ({ crates: state.crates.map((crate, _i) => { - if (crate === crate.id) { + if (crate_id === crate.id) { return { ...crate, - items: crate.items.splice(index, 1) + items: crate.items.toSpliced(index, 1) } } else return crate; }) })), - clearCrate: (id) => set(state => ({ + _clearCrate: (id) => set(state => ({ crates: state.crates.map((crate, _i) => { if (id === crate.id) { return { @@ -141,9 +162,12 @@ const useCart = ((set, get) => ({ else return crate; }) })), - updateOptions: (crate, index, new_options) => set(state => ({ + clearAll: () => set(state => ({ + crates: [] + })), + _updateOptions: (crate_id, index, new_options) => set(state => ({ crates: state.crates.map((crate, _i) => { - if (crate === crate.id) { + if (crate_id === crate.id) { let itemsCopy = Array.from(crate.items); itemsCopy[index].options_data = {...itemsCopy[index].options_data, ...new_options}; return { @@ -154,37 +178,74 @@ const useCart = ((set, get) => ({ else return crate; }) })), - highlightCard: (crate, index) => set(state => ({ - highlighted: { - crate: crate, - card: index - } - })), - highlightReset: () => set(state => ({ - highlighted: { - crate: "", - card: 0 - } - })), - fillWarnings: (crate) => set(state => ({ - // actually seems to be just render-time action, no need to put data in it, - // though needs to be optimized to be done only on real crate updates + fillWarnings: (crate_id) => set(state => ({ crates: state.crates.map((crate, _i) => { - if (crate === crate.id) { + if (crate_id === crate.id) { let itemsCopy = Array.from(crate.items); itemsCopy = FillResources(itemsCopy); itemsCopy = TriggerWarnings(itemsCopy); - const crate_warnings = TriggerCrateWarnings(crate); + const [crate_warnings, occupied] = TriggerCrateWarnings(crate); return { ...crate, items: itemsCopy, - warnings: crate_warnings + warnings: crate_warnings, + occupiedHP: occupied } } else return crate; }) - })) + })), + + totalOrderPrice: () => { + let sum = 0; + get().crates.forEach( (crate, _i) => { + sum += get().crate_modes[crate.crate_mode].price; + crate.items.forEach((item, _) => { + sum += item.price; + }); + }); + return sum; + }, + + // Composite actions that require warnings recalculation: + + newCrate: () => { + const crate_id = "crate" + get().crates.length; + get()._newCrate(crate_id) + get().fillWarnings(crate_id); + }, + + setCrateMode: (id, mode) => { + console.log("setCrateMode", id, mode) + get()._setCrateMode(id, mode) + get().fillWarnings(id); + }, + + addCardFromBacklog: (crate_to, index_from, index_to) => { + const dest = crate_to || get().active_crate; + get()._addCardFromBacklog(dest, index_from, index_to) + get().fillWarnings(dest); + }, + + moveCard: (crate_from, index_from, crate_to, index_to) => { + get()._moveCard(crate_from, index_from, crate_to, index_to); + get().fillWarnings(crate_to); + if (crate_from !== crate_to) get().fillWarnings(crate_from); + }, + deleteCard: (crate_id, index) => { + get()._deleteCard(crate_id, index); + get().fillWarnings(crate_id); + }, + clearCrate: (id) => { + get()._clearCrate(id); + get().fillWarnings(id); + }, + + updateOptions: (crate_id, index, new_options) => { + get()._updateOptions(crate_id, index, new_options); + get().fillWarnings(crate_id); + } // TODO load and save jsons? })) @@ -195,5 +256,6 @@ export const useShopStore = create((...params) => ({ ...useCrateModes(...params), ...useCart(...params), ...useSubmitForm(...params), - ...useLayout(...params) + ...useLayout(...params), + ...useHighlighted(...params), })) \ No newline at end of file diff --git a/static/js/shop/warnings.js b/static/js/shop/warnings.js index fc325ce..224c135 100644 --- a/static/js/shop/warnings.js +++ b/static/js/shop/warnings.js @@ -7,10 +7,11 @@ import {crate_type_to_hp, item_occupied_counters, resource_counters} from "./count_resources"; import {data as shared_data} from "./utils"; +import {useShopStore} from "./shop_store"; const Levels = { - "reminder": {priority: 1, icon: '/images/shop/icon-reminder.svg'}, - "warning": {priority: 2, icon: '/images/shop/icon-warning.svg'}, + "reminder": {priority: 1, icon: '/images/shop/icon-reminder.svg', color: "black"}, + "warning": {priority: 2, icon: '/images/shop/icon-warning.svg', color: "#c75e5e"}, } const find_in_counters = (counters, name) => { @@ -97,21 +98,6 @@ const Types = { } } - -export function TriggerCardWarnings(data, index, precounted) { - const element = data[index]; - return (element.warnings && element.warnings - .map((warning, _) => { - if (!!Types[warning]) - return Types[warning].trigger(data, index, precounted) ? {trigger: undefined, name: warning, ...Types[warning]} : null; - else - return Types.default; - }) - .filter((warning, _) => { - return !!warning - })); -} - export function TriggerWarnings(data) { return data.map((element, index) => { if (!element.warnings) return element; @@ -137,23 +123,26 @@ export function MaxLevel(warnings) { return mx; } - +export function LevelUI(warning_level) { + const warning_t = Levels[warning_level]; + return {icon: warning_t.icon, color: warning_t.color}; +} const crate_warnings = { "overfit": { message: "You have reached the maximum number of slots allowed for this crate. Consider removing cards.", level: "warning", trigger: (crate, occupied) => { - const nbrHP = crate_type_to_hp(crate.crate_type); - return occupied > nbrHP; + const nbrHP = useShopStore.getState().crateParams(crate.crate_mode).hp; + return occupied > nbrHP && nbrHP > 0; } }, "underfit_rack": { message: "The selected cards fit in a 42hp desktop crate, consider switching to it for a more compact system", level: "reminder", trigger: (crate, occupied) => { - const nbrHPDesktop = shared_data.crateModes.desktop.hp; - return crate.crate_type === shared_data.crateModes.rack.id && occupied < nbrHPDesktop; + const nbrHPDesktop = useShopStore.getState().crate_modes.desktop.hp; + return crate.crate_mode === useShopStore.getState().crate_modes.rack.id && occupied < nbrHPDesktop; } } } @@ -164,5 +153,5 @@ export function TriggerCrateWarnings(crate) { Object.entries(crate_warnings).forEach(([id, warning], _) => { if (warning.trigger(crate, nbrOccupied)) warnings.push({...warning, id: id, trigger: undefined}); }) - return warnings; + return [warnings, nbrOccupied]; } \ No newline at end of file diff --git a/static/js/shop_data.js b/static/js/shop_data.js index ca685f9..39b0513 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -70,7 +70,7 @@ const shop_data = { name_number: '1124', name_codename: 'Kasli 2.0', price: 3600, - image: '/shop/graphic-03_kasli.svg', + image: '/images/shop/graphic-03_kasli.svg', specs: [ 'FPGA core device, runs ARTIQ kernels, controls the EEMs.', '4 SFP 6Gb/s slots for Ethernet or DRTIO.', @@ -126,7 +126,7 @@ const shop_data = { name_number: '1125', name_codename: 'Kasli-SoC', price: 5100, - image: '/shop/graphic-03_kaslisoc.svg', + image: '/images/shop/graphic-03_kaslisoc.svg', specs: [ 'Core device based on Zynq-7000 CPU+FPGA system-on-chip.', 'Runs ARTIQ kernels on 1GHz Cortex-A9 CPU with hardware FPU.', @@ -194,7 +194,7 @@ const shop_data = { name_number: '1008', name_codename: '', price: 400, - image: '/shop/graphic-03_VHDCI_carrier.svg', + image: '/images/shop/graphic-03_VHDCI_carrier.svg', specs: [ 'Passive adapter between VHDCI and EEMs.', 'VHDCI (SCSI-3) cables can carry EEM signals over short distances between crates.', @@ -221,7 +221,7 @@ const shop_data = { name_number: '2118', name_codename: '', price: 450, - image: '/shop/graphic-03_BNC-TTL.svg', + image: '/images/shop/graphic-03_BNC-TTL.svg', specs: [ 'Two banks of four digital channels each, with BNC connectors.', 'Each bank with individual ground isolation.', @@ -293,7 +293,7 @@ const shop_data = { name_number: '2128', name_codename: '', price: 400, - image: '/shop/graphic-03_SMA-TTL.svg', + image: '/images/shop/graphic-03_SMA-TTL.svg', specs: [ 'Same as above, but with SMA connectors.' ], @@ -359,7 +359,7 @@ const shop_data = { name_number: '2238', name_codename: '', price: 600, - image: '/shop/graphic-03_MCX-TTL.svg', + image: '/images/shop/graphic-03_MCX-TTL.svg', specs: [ '16 single-ended digital signals on MCX connectors.', 'Direction selectable in banks of four signals.', @@ -451,7 +451,7 @@ const shop_data = { name_number: '2245', name_codename: '', price: 390, - image: '/shop/graphic-03_LVDS.svg', + image: '/images/shop/graphic-03_LVDS.svg', specs: [ 'Supplies 16 LVDS pairs via 4 front-panel RJ45 connectors.', 'Each RJ45 supplies 4 LVDS DIOs.', @@ -538,7 +538,7 @@ const shop_data = { name_number: '4410', name_codename: 'Urukul', price: 2350, - image: '/shop/graphic-03_Urukul.svg', + image: '/images/shop/graphic-03_Urukul.svg', specs: [ '4 channel 1GS/s DDS.', 'Output frequency (-3 dB): <1 to >400 MHz.', @@ -599,7 +599,7 @@ const shop_data = { name_number: '4412', name_codename: 'Urukul', price: 2350, - image: '/shop/graphic-03_Urukul-4412.svg', + image: '/images/shop/graphic-03_Urukul-4412.svg', specs: [ '4 channel 1GS/s DDS.', 'Higher frequency resolution ~8 µHz (47 bit)', @@ -635,7 +635,7 @@ const shop_data = { name_number: '4624', name_codename: 'Phaser', price: 4260, - image: '/shop/graphic-03_Phaser.svg', + image: '/images/shop/graphic-03_Phaser.svg', specs: [ '2x 1.25 GS/s IQ upconverters.', 'dual IQ mixer + 0.3 GHz to 4.8 GHz VCO + PLL.', @@ -667,7 +667,7 @@ const shop_data = { name_number: '5432', name_codename: 'Zotino', price: 1600, - image: '/shop/graphic-03_Zotino.svg', + image: '/images/shop/graphic-03_Zotino.svg', specs: [ '32-channel DAC.', '16-bit resolution.', @@ -702,7 +702,7 @@ const shop_data = { name_number: '5632', name_codename: 'Fastino', price: 3390, - image: '/shop/graphic-03_Fastino.svg', + image: '/images/shop/graphic-03_Fastino.svg', specs: [ '32-channel DAC.', '16-bit resolution.', @@ -731,7 +731,7 @@ const shop_data = { name_number: '5518', name_codename: '', price: 160, - image: '/shop/graphic-03_IDC-BNC-adapter.svg', + image: '/images/shop/graphic-03_IDC-BNC-adapter.svg', specs: [ 'Breaks out analog signals from Zotino or HD68-IDC to BNC connectors.', 'Each card provides 8 channels.', @@ -753,7 +753,7 @@ const shop_data = { name_number: '5528', name_codename: '', price: 160, - image: '/shop/graphic-03_SMA-IDC.svg', + image: '/images/shop/graphic-03_SMA-IDC.svg', specs: [ 'Breaks out analog signals from Zotino or HD68-IDC to SMA connectors.', 'Each card provides 8 channels.', @@ -775,7 +775,7 @@ const shop_data = { name_number: '5538', name_codename: '', price: 160, - image: '/shop/graphic-03_MCX-IDC.svg', + image: '/images/shop/graphic-03_MCX-IDC.svg', specs: [ 'Breaks out analog signals from Zotino or HD68-IDC to MCX connectors.', 'Each card provides 8 channels.', @@ -797,7 +797,7 @@ const shop_data = { name_number: '5568', name_codename: '', price: 150, - image: '/shop/graphic-03_HD68.svg', + image: '/images/shop/graphic-03_HD68.svg', specs: [ 'Connects an external HD68 cable to IDC-BNC, IDC-SMA or IDC-MCX cards.', ], @@ -823,7 +823,7 @@ const shop_data = { name_number: '5108', name_codename: '', price: 1600, - image: '/shop/graphic-03_Sampler.svg', + image: '/images/shop/graphic-03_Sampler.svg', specs: [ '8-channel ADC.', '16-bit resolution.', @@ -874,7 +874,7 @@ const shop_data = { name_number: '6302', name_codename: '', price: 550, - image: '/shop/graphic-03_Grabber.svg', + image: '/images/shop/graphic-03_Grabber.svg', specs: [ 'Camera input interface card.', 'Supports some EMCCD cameras.', @@ -901,7 +901,7 @@ const shop_data = { name_number: '7210', name_codename: '', price: 525, - image: '/shop/graphic-03_Clocker.svg', + image: '/images/shop/graphic-03_Clocker.svg', specs: [ 'Distribute a low jitter clock signal among cards.', '2 inputs.', @@ -936,7 +936,7 @@ const shop_data = { name_number: '8452', name_codename: 'Stabilizer', price: 2000, - image: '/shop/graphic-03_Stabilizer.svg', + image: '/images/shop/graphic-03_Stabilizer.svg', specs: [ 'CPU-based dual-channel fast servo.', '400MHz STM32H743ZIT6.', @@ -968,7 +968,7 @@ const shop_data = { name_number: '4456', name_codename: 'Mirny', price: 2660, - image: '/shop/graphic-03_Mirny.svg', + image: '/images/shop/graphic-03_Mirny.svg', specs: [ '4-channel Wide-band PLL/VCO-based microwave frequency synthesiser.', '53 MHz to >4 GHz.', @@ -998,7 +998,7 @@ const shop_data = { name_number: '4457', name_codename: 'Mirny + Almazny', price: 3660, - image: '/shop/graphic-03_Almazny.svg', + image: '/images/shop/graphic-03_Almazny.svg', specs: [ 'Mirny with high frequency mezzanine.', 'Additional 4 channels up to 12 GHz.', @@ -1025,7 +1025,7 @@ const shop_data = { name_number: '8453', name_codename: '', price: 2600, - image: '/shop/graphic-03_Thermostat-EEM.svg', + image: '/images/shop/graphic-03_Thermostat-EEM.svg', specs: [ '4 TEC channels.', 'Sensor channel count: 8 differential, 16 single-ended.', @@ -1053,7 +1053,7 @@ const shop_data = { name_number: '5716', name_codename: 'Shuttler', price: 8500, - image: '/shop/graphic-03_Shuttler.svg', + image: '/images/shop/graphic-03_Shuttler.svg', specs: [ '16-ch, 125 MSPS DAC EEM with remote analog front end board.', 'High DC resolution (up to ~18 bits with sigma-delta modulation) for trap electrode bias.', @@ -1080,7 +1080,7 @@ const shop_data = { name_number: '4459', name_codename: 'Stabilizer + Pounder', price: 4460, - image: '/shop/graphic-03_Pounder.svg', + image: '/images/shop/graphic-03_Pounder.svg', specs: [ 'Stabilizer with Pounder daughter card.', '2-channel Pound Drever Hall (PDH) lock generator.', @@ -1111,7 +1111,7 @@ const shop_data = { name_number: '1106', name_codename: '', price: 750, - image: '/shop/graphic-03_eem_pwr_mod.svg', + image: '/images/shop/graphic-03_eem_pwr_mod.svg', specs: [ "EEM AC power module.", "400W with forced cooling (25CFM), 200W with free air convection.", @@ -1189,7 +1189,8 @@ const shop_data = { id: "crate0", crate_mode: "rack", items: [], - warnings: [] + warnings: [], + occupiedHP: 0, }] },