diff --git a/static/js/shop/Cart.jsx b/static/js/shop/Cart.jsx index dc54471..9a1b7e5 100644 --- a/static/js/shop/Cart.jsx +++ b/static/js/shop/Cart.jsx @@ -1,101 +1,72 @@ -import React, {PureComponent} from 'react' -import PropTypes from "prop-types"; +import React from 'react' import {Droppable} from "@hello-pangea/dnd"; import {cartStyle, nbrOccupiedSlotsInCrate} from "./utils"; import {ProductCartItem} from "./ProductCartItem.jsx"; import {FakePlaceholder} from "./FakePlaceholder.jsx"; import {FillExtData} from "./options/utils"; +import {crate_type_to_hp, hp_to_slots, resource_counters} from "./count_resources"; /** * Component that displays a list of */ -export class Cart extends PureComponent { - - static get propTypes() { - return { - isMobile: PropTypes.bool, - isTouch: PropTypes.bool, - nbrSlots: PropTypes.number, - itemHovered: PropTypes.string, - data: PropTypes.object.isRequired, - onToggleOverlayRemove: PropTypes.func, - onClickRemoveItem: PropTypes.func, - onCardUpdate: PropTypes.func, - onClickItem: PropTypes.func, - }; - } - - render() { - const { - isMobile, - isTouch, - nbrSlots, - itemHovered, - data, - onToggleOverlayRemove, - onClickRemoveItem, - onClickItem, - onCardUpdate, - } = this.props; - - const nbrOccupied = nbrOccupiedSlotsInCrate(data.items); - - const products = data.items.map((item, index) => { - let itemData; - let ext_data = FillExtData(data.itemsData, index); - if (data.itemsData && index in data.itemsData) { - itemData = data.itemsData[index]; - } - return ( - = nbrSlots} - data={itemData} - ext_data={ext_data} - onToggleOverlayRemove={onToggleOverlayRemove} - onClickRemoveItem={onClickRemoveItem} - onCardUpdate={onCardUpdate} - onClickItem={onClickItem} - model={item}> - - ); - }); +export function Cart({isMobile, isTouch, data, onToggleOverlayRemove, onClickRemoveItem, onCardUpdate, onClickItem}) { + const nbrOccupied = resource_counters.hp(data.items); + const nbrSlots = hp_to_slots(crate_type_to_hp(data.crate_type)); + const products = data.items.map((item, index) => { + let itemData; + let ext_data = FillExtData(data.items, index); + if (data.items && index in data.items) { + itemData = data.items[index]; + } return ( - - - {(provided, snapshot) => ( -
- - {products} - - {provided.placeholder && ( -
- {provided.placeholder} -
- )} - - -
- )} - -
+ = nbrSlots} + data={itemData} + ext_data={ext_data} + onToggleOverlayRemove={onToggleOverlayRemove} + onClickRemoveItem={onClickRemoveItem} + onCardUpdate={onCardUpdate} + onClickItem={onClickItem} + model={item}> + ); - } + }); + + 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 ef9bf80..0b9fabb 100644 --- a/static/js/shop/Crate.jsx +++ b/static/js/shop/Crate.jsx @@ -1,44 +1,36 @@ -import React, {PureComponent} from 'react'; -import PropTypes from "prop-types"; +import React from 'react'; +import {Cart} from "./Cart.jsx"; +import {CrateMode} from "./CrateMode.jsx"; +import {CrateWarnings} from "./CrateWarnings.jsx"; /** * Component that displays the main crate with reminder rules. * It includes and rules */ -export class Crate extends PureComponent { +export function Crate({data, handleToggleOverlayRemove, handleDeleteItem, handleShowOverlayRemove, handleCardsUpdated, isMobile, isTouch}) { + return ( +
- static get propTypes() { - return { - rules: PropTypes.array, - cart: PropTypes.element, - }; - } + - render() { - const { - rules, - cart, - } = this.props; +
- return ( -
- -
- - {cart} - - {rules && rules.length > 0 && ( -
- {rules.map((rule, index) => ( -

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

- ))} -
- )} -
+ + + {1||(rules && rules.length > 0) && ( + + )}
- ); - } + +
+ ); + } \ No newline at end of file diff --git a/static/js/shop/CrateList.jsx b/static/js/shop/CrateList.jsx new file mode 100644 index 0000000..89cc1e4 --- /dev/null +++ b/static/js/shop/CrateList.jsx @@ -0,0 +1,17 @@ +import React from 'react' +import {Accordion} from "react-bootstrap"; +import {Crate} from "./Crate.jsx"; + +export function CrateList({crates, isMobile, isTouch}) { + return ( + + {Object.entries(crates).map(([crate_id, crate], index) => + + Crate #{`${index}`} + + + + + )} + ) +} \ No newline at end of file diff --git a/static/js/shop/CrateMode.jsx b/static/js/shop/CrateMode.jsx index 121be7b..953c26f 100644 --- a/static/js/shop/CrateMode.jsx +++ b/static/js/shop/CrateMode.jsx @@ -1,48 +1,22 @@ -import React, {PureComponent} from 'react'; -import PropTypes from "prop-types"; +import React from 'react'; +import {data as shared_data} from "./utils"; /** * Component that displays crate modes */ -export class CrateMode extends PureComponent { +export function CrateMode({current, onChange}) { + return ( +
+ {shared_data.crateModeOrder.map(item => ( + {shared_data.crateModes[item].name} + ))} +
+ ); +} - constructor(props) { - super(props); - this.handleOnClickMode = this.handleOnClickMode.bind(this); - } - - handleOnClickMode(mode, e) { - if (this.props.onClickMode) { - this.props.onClickMode(mode); - } - e.preventDefault(); - } - - render() { - const { - mode, - items, - } = this.props; - - return ( -
- {items.map(item => ( - {item.name} - ))} -
- ); - } -} \ No newline at end of file +//onClick={onChange(this, item)} \ No newline at end of file diff --git a/static/js/shop/CrateWarnings.jsx b/static/js/shop/CrateWarnings.jsx new file mode 100644 index 0000000..e9887c5 --- /dev/null +++ b/static/js/shop/CrateWarnings.jsx @@ -0,0 +1,13 @@ +import React from "react"; + +export function CrateWarnings() { + return ( +
+ {rules.map((rule, index) => ( +

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

+ ))} +
+ ) +} \ No newline at end of file diff --git a/static/js/shop/FakePlaceholder.jsx b/static/js/shop/FakePlaceholder.jsx index 2eb3abc..00f1a34 100644 --- a/static/js/shop/FakePlaceholder.jsx +++ b/static/js/shop/FakePlaceholder.jsx @@ -1,47 +1,27 @@ -import React, {PureComponent} from 'react' -import PropTypes from "prop-types"; -import {nbrOccupiedSlotsInCrate} from "./utils"; +import React from 'react'; /** * Component that displays a placeholder inside crate. * Allows to display how it remains space for the current crate. */ -export class FakePlaceholder extends PureComponent { +export function FakePlaceholder({isDraggingOver, nToDraw}) { + const fakePlaceholder = []; - static get propTypes() { - return { - isDraggingOver: PropTypes.bool, - nbrSlots: PropTypes.number.isRequired, - items: PropTypes.array.isRequired, - }; - } - - render() { - const { - isDraggingOver, - nbrSlots, - items, - } = this.props; - - const fakePlaceholder = []; - const nbrOccupied = nbrOccupiedSlotsInCrate(items); - - for (var i = (nbrSlots - nbrOccupied); i > 0; i--) { - fakePlaceholder.push( -
- ); - } - - return ( - - {fakePlaceholder} - + for (let i = nToDraw; i > 0; i--) { + fakePlaceholder.push( +
); - } + + return ( + + {fakePlaceholder} + + ); + } diff --git a/static/js/shop/ImportJSON.jsx b/static/js/shop/ImportJSON.jsx new file mode 100644 index 0000000..bad145d --- /dev/null +++ b/static/js/shop/ImportJSON.jsx @@ -0,0 +1,5 @@ +export function ImportJSON() { + return ( +
Import JSON BAOBAO
+ ) +} \ No newline at end of file diff --git a/static/js/shop/OrderPanel.jsx b/static/js/shop/OrderPanel.jsx index 5c7c970..fff6f41 100644 --- a/static/js/shop/OrderPanel.jsx +++ b/static/js/shop/OrderPanel.jsx @@ -11,8 +11,7 @@ export class OrderPanel extends PureComponent { return { title: PropTypes.string, description: PropTypes.element, - crateMode: PropTypes.element, - crate: PropTypes.element, + cratesList: PropTypes.element, summaryPrice: PropTypes.element, form: PropTypes.element, isMobile: PropTypes.bool, @@ -25,8 +24,7 @@ export class OrderPanel extends PureComponent { const { title, description, - crateMode, - crate, + cratesList, summaryPrice, form, isMobile, @@ -41,8 +39,6 @@ export class OrderPanel extends PureComponent {
{description} - - {crateMode}
@@ -61,7 +57,7 @@ export class OrderPanel extends PureComponent {
) : null} - {crate} + {cratesList}
{summaryPrice} diff --git a/static/js/shop/OrderSummary.jsx b/static/js/shop/OrderSummary.jsx index 68bb421..9e81175 100644 --- a/static/js/shop/OrderSummary.jsx +++ b/static/js/shop/OrderSummary.jsx @@ -121,7 +121,7 @@ export class OrderSummary extends PureComponent { - {summary.map((item, index) => { + {[].map((item, index) => { let alert, warning, options, options_data; if (itemsData[index] && itemsData[index].warnings) { diff --git a/static/js/shop/Shop.jsx b/static/js/shop/Shop.jsx index a841f10..41e4d9d 100644 --- a/static/js/shop/Shop.jsx +++ b/static/js/shop/Shop.jsx @@ -4,18 +4,16 @@ 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 {copyFromBacklog, 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"; import {TriggerWarnings} from "./warnings"; import {FillResources} from "./count_resources"; +import {CrateList} from "./CrateList.jsx"; /** * Component that render the entire shop @@ -64,14 +62,14 @@ export class Shop extends PureComponent { ], }; const destination = { - droppableId: 'cart', + droppableId: 'crate0', index: 0, }; - this.handleOnDragEnd({ + /*this.handleOnDragEnd({ source, destination - }); + });*/ } componentDidUpdate(prevProps, prevState) { @@ -83,13 +81,15 @@ export class Shop extends PureComponent { * trigger again this function (componentDidUpdate) and thus, * making an infinite loop. */ + return; if ( - (prevState.columns.cart.items !== this.state.columns.cart.items) || + (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({ @@ -353,11 +353,20 @@ export class Shop extends PureComponent { }) } - handleOnDragEnd(result, newAdded) { + 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, _) => { @@ -365,82 +374,61 @@ export class Shop extends PureComponent { }) } else if (source.index >= 0) { dragged_items.push(itemsUnfoldedList[source.index]); - } + }*/ + console.log('==> result', result); + // dropped outside the list 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; - + switch (source.droppableId) { + // TODO add delete functionality 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, - ), - }, - }, + 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 + )}} + } }); break; - default: + this.setState({ + columns: move( + this.state.columns[source.droppableId], + this.state.columns[destination.droppableId], + source, + destination + ) + }); break; } } @@ -460,6 +448,7 @@ export class Shop extends PureComponent { checkAlerts(newItems) { console.log('--- START CHECKING CRATE WARNING ---'); + return; const { currentMode, @@ -546,7 +535,7 @@ export class Shop extends PureComponent { @@ -559,37 +548,17 @@ export class Shop extends PureComponent { isMobile={isMobile} title="Order hardware" 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 sales@m-labs.hk. The price is estimated and must be confirmed by a quote.

)} - crateMode={ - - } - crate={ - - - } - rules={Object.values(rules).filter(rule => rule)}> - + cratesList={ + } summaryPrice={ { if (!item.options_data @@ -45,7 +46,7 @@ function CounterFactory(name) { } } -const resource_counters = { +export const resource_counters = { "eem": CounterFactory("eem"), "clk": CounterFactory("clk"), "idc": CounterFactory("idc"), @@ -70,4 +71,12 @@ export function FillResources(data) { element.counted_resources = CountResources(data, index); return element; }) +} + +export function hp_to_slots(hp) { + return Math.trunc(hp / 4); +} + +export function crate_type_to_hp(crate_t) { + return shared_data.crateModes[crate_t].hp; } \ No newline at end of file diff --git a/static/js/shop/utils.js b/static/js/shop/utils.js index 30eda86..793e9fd 100644 --- a/static/js/shop/utils.js +++ b/static/js/shop/utils.js @@ -5,7 +5,7 @@ import {v4 as uuidv4} from "uuid"; export const data = window.shop_data; export const itemsUnfoldedList = Array.from(data.columns.backlog.categories.map(groupId => groupId.itemIds).flat()); -export const copy = ( +/*export const copy = ( model, source, destination, @@ -23,7 +23,7 @@ export const copy = ( return destClone; }; - +*/ export const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); @@ -38,6 +38,36 @@ export const remove = (list, startIndex) => { return result; }; + +export const copyFromBacklog = (source, destination, droppableSource, droppableDestination) => { + console.log('==> dest', destination); + + const destClone = Array.from(destination.items); + const items = droppableSource.indexes + ? droppableSource.indexes .map((item, _) => itemsUnfoldedList[item]) + : [itemsUnfoldedList[droppableSource.index]]; + + destClone.splice(droppableDestination.index, 0, ...items.map((item, _) => { + return {...source[item], id: uuidv4()} + })); + return destClone; +}; + +export const move = (source, destination, droppableSource, droppableDestination) => { + console.log('==> move', source, destination); + const sourceClone = Array.from(source); + const destClone = Array.from(destination); + const [removed] = sourceClone.splice(droppableSource.index, 1); + + destClone.splice(droppableDestination.index, 0, removed); + + const result = {columns: {}}; + result.columns[droppableSource.droppableId] = sourceClone; + result.columns[droppableDestination.droppableId] = destClone; + + return result; +}; + export const productStyle = (style, snapshot, removeAnim, hovered, selected, cart=false) => { const custom = { opacity: snapshot.isDragging ? .7 : 1, @@ -76,13 +106,6 @@ export const cartStyle = (style, snapshot) => { }; } - -export const nbrOccupiedSlotsInCrate = (items) => { - return items.reduce((prev, next) => { - return prev + (next.hp === 8 ? 2 : 1); - }, 0); -}; - export 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 diff --git a/static/js/shop_data.js b/static/js/shop_data.js index 5db2cdc..da6c7a4 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -38,6 +38,30 @@ const shop_data = { price: 500, }], + crateModes: { + rack: { + id: 'rack', + name: 'Rack mountable crate', + price: 550, + hp: 84 + }, + desktop: { + id: 'desktop', + name: 'Desktop crate', + price: 500, + hp: 42 + }, + no_crate: { + id: 'no_crate', + name: 'Standalone cards', + price: 0, + hp: -1 + } + }, + crateModeOrder: [ + "rack", "desktop", "no_crate" + ], + items: { /* keys are also ids, avoid changing them */ 'kasli': { @@ -1161,12 +1185,12 @@ const shop_data = { ], }, - 'cart': { - id: 'cart', - title: 'Cart', - items: [], - itemsData: [], - }, + "crates": { + "crate0": { + crate_type: "rack", + items: [] + } + } },