Add fan tray option to the crate
Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
parent
3366f80ed7
commit
ddd8b2d894
File diff suppressed because one or more lines are too long
@ -6,6 +6,7 @@ import {useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
import {CrateFanTray} from "./CrateFanTray";
|
||||
|
||||
|
||||
/**
|
||||
@ -43,6 +44,8 @@ export function Crate({crate_index}) {
|
||||
<Cart crate_index={crate_index}/>
|
||||
|
||||
<CrateWarnings crate_index={crate_index} />
|
||||
|
||||
<CrateFanTray crate_index={crate_index}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
37
static/js/shop/CrateFanTray.jsx
Normal file
37
static/js/shop/CrateFanTray.jsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {Tip} from "./options/components/Tip";
|
||||
import {formatMoney} from "./utils";
|
||||
|
||||
export function CrateFanTray({crate_index}) {
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const fanTray = useShopStore((state) => state.fanTray);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const fanTrayAvailable = useShopStore((state) => state.fanTrayAvailableByIndex(crate_index));
|
||||
const fanTrayEnabled = useShopStore((state) => state.crates[crate_index].fan_tray);
|
||||
const updateFanTray = useShopStore((state) => state.updateFanTrayOption);
|
||||
|
||||
const base_id = crate_id + "fan_tray";
|
||||
return fanTrayAvailable ? (
|
||||
<div className="crate-bar">
|
||||
<div className="shop-switch">
|
||||
<div className="form-check form-switch">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
id={base_id}
|
||||
checked={fanTrayEnabled}
|
||||
onClick={() => updateFanTray(crate_id, !fanTrayEnabled)}
|
||||
onChange={() => updateFanTray(crate_id, !fanTrayEnabled)}
|
||||
/>
|
||||
<label className="form-check-label" htmlFor={base_id} style={{"display": "inline", marginRight: "0.125rem"}}>
|
||||
{fanTray.optionTitle} (+{`${currency} ${formatMoney(fanTray.price)}`})
|
||||
</label>
|
||||
{fanTray.tip && <Tip id={base_id + "tooltip"} tip={fanTray.tip}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
@ -6,6 +6,7 @@ import {SummaryCrateCard} from "./SummaryCrateCard";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
import {SummaryCrateFanTray} from "./SummaryCrateFanTray";
|
||||
|
||||
export function SummaryCrate({crate_index}) {
|
||||
// #!render_count
|
||||
@ -25,6 +26,7 @@ export function SummaryCrate({crate_index}) {
|
||||
{range(0, crate_len).map((index, _i) =>
|
||||
<SummaryCrateCard crate_index={crate_index} card_index={index} key={"summary_crate_" + crate_id + "_" +index} />
|
||||
)}
|
||||
<SummaryCrateFanTray crate_index={crate_index}/>
|
||||
</tbody>
|
||||
)
|
||||
}
|
45
static/js/shop/SummaryCrateFanTray.jsx
Normal file
45
static/js/shop/SummaryCrateFanTray.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
import {formatMoney} from "./utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
|
||||
|
||||
export function SummaryCrateFanTray({crate_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
const fanTray = useShopStore((state) => state.fanTray);
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
const fanTrayAvailable = useShopStore((state) => state.fanTrayAvailableByIndex(crate_index));
|
||||
const fanTrayEnabled = useShopStore((state) => state.crates[crate_index].fan_tray);
|
||||
const updateFanTray = useShopStore((state) => state.updateFanTrayOption);
|
||||
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrateCard renders: ", renderCount)
|
||||
|
||||
return (fanTrayAvailable && fanTrayEnabled) ? (<tr
|
||||
key={"summary_crate_" + crate_id + "_fan_tray"}>
|
||||
<td className="item-card-name">
|
||||
<span style={{
|
||||
'display': 'inline-block', 'width': '16px',
|
||||
}}> </span>
|
||||
<div>{fanTray.optionTitle}</div>
|
||||
</td>
|
||||
|
||||
<td className="price">
|
||||
<div className="d-inline-flex align-content-center">
|
||||
{`${currency} ${formatMoney(fanTray.price)}`}
|
||||
|
||||
<button onClick={() => updateFanTray(crate_id, false)}>
|
||||
<img src="/images/shop/icon-remove.svg" className="d-block"/>
|
||||
</button>
|
||||
|
||||
<div style={{'width': '45px', 'height': '20px'}} className="d-inline"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>) : null;
|
||||
}
|
@ -36,10 +36,12 @@ export function JSONToCrates(description) {
|
||||
const parsed = JSON.parse(description);
|
||||
const crates_raw = parsed.crates;
|
||||
const pn_to_card = useShopStore.getState().getCardDescriptionByPn;
|
||||
const fanTrayAvailable = useShopStore.getState().fanTrayAvailableForMode;
|
||||
|
||||
const crates = Array.from(crates_raw.map((crate, c_i) => ({
|
||||
id: crate.type === "no_crate" ? "spare" : "crate" + c_i,
|
||||
name: crate.type === "no_crate" ? "Spare cards" : undefined,
|
||||
fan_tray: fanTrayAvailable(crate.type) && crate.fan_tray === true,
|
||||
crate_mode: crate.type,
|
||||
items: Array.from(crate.items.map((card, _i) => ({
|
||||
...pn_to_card(card.pn),
|
||||
@ -57,6 +59,7 @@ export function JSONToCrates(description) {
|
||||
}
|
||||
|
||||
export function CratesToJSON(crates) {
|
||||
const fanTrayAvailable = useShopStore.getState().fanTrayAvailableForMode;
|
||||
return JSON.stringify({
|
||||
// additional fields can go here
|
||||
crates: Array.from(crates.map((crate, _i) => ({
|
||||
@ -64,7 +67,8 @@ export function CratesToJSON(crates) {
|
||||
pn: card.name_number,
|
||||
options: (card.options_data && card.options) ? FilterOptions(card.options, card.options_data) : null
|
||||
}))),
|
||||
type: crate.crate_mode
|
||||
type: crate.crate_mode,
|
||||
fan_tray: (!fanTrayAvailable(crate.crate_mode) ? undefined : true) && crate.fan_tray
|
||||
})))
|
||||
}, null, 2)
|
||||
}
|
@ -34,6 +34,13 @@ const useCrateModes = ((set, get) => ({
|
||||
crateParams: mode => get().crate_modes[mode],
|
||||
}));
|
||||
|
||||
const useFanTray = ((set, get) => ({
|
||||
fanTray: shared_data.fanTray,
|
||||
fanTrayAvailableForMode: (crate_mode) => {
|
||||
return get().fanTray.crateModesAvailable[crate_mode] === true;
|
||||
},
|
||||
}));
|
||||
|
||||
const useLayout = ((set, get) => ({
|
||||
isTouch: window.isTouchEnabled(),
|
||||
isMobile: window.deviceIsMobile(),
|
||||
@ -393,8 +400,9 @@ const useCart = ((set, get) => ({
|
||||
|
||||
totalOrderPrice: () => {
|
||||
let sum = 0;
|
||||
get().crates.forEach( (crate, _i) => {
|
||||
get().crates.forEach( (crate, i) => {
|
||||
sum += get().crate_modes[crate.crate_mode].price;
|
||||
sum += (crate.fan_tray && get().fanTrayAvailableByIndex(i)) ? get().fanTray.price : 0;
|
||||
crate.items.forEach((item, _) => {
|
||||
sum += item.price;
|
||||
});
|
||||
@ -459,6 +467,22 @@ const useCart = ((set, get) => ({
|
||||
get()._updateOptions(crate_id, index, new_options);
|
||||
get().fillExtData(crate_id);
|
||||
get().fillWarnings(crate_id);
|
||||
},
|
||||
|
||||
updateFanTrayOption: (crate_id, add_fan_tray) => set(state => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
return {
|
||||
...crate,
|
||||
fan_tray: add_fan_tray
|
||||
}
|
||||
}
|
||||
else return crate;
|
||||
})
|
||||
})),
|
||||
|
||||
fanTrayAvailableByIndex: (crate_index) => {
|
||||
return get().fanTrayAvailableForMode(get().crates[crate_index].crate_mode);
|
||||
}
|
||||
}))
|
||||
|
||||
@ -471,4 +495,5 @@ export const useShopStore = createWithEqualityFn((...params) => ({
|
||||
...useLayout(...params),
|
||||
...useHighlighted(...params),
|
||||
...useImportJSON(...params),
|
||||
...useFanTray(...params),
|
||||
}))
|
@ -71,7 +71,7 @@ const Types = {
|
||||
"no_idc_source": {
|
||||
level: "warning",
|
||||
trigger: no_source_trigger("idc"),
|
||||
message: 'Should be after a Zotino or a HD68-IDC or with another IDC-BNC.'
|
||||
message: 'Should be after a Zotino or a HD68-IDC or with another IDC adapter.'
|
||||
},
|
||||
"clk_resource": {
|
||||
level: "warning",
|
||||
|
@ -28,6 +28,15 @@ const shop_data = {
|
||||
"rack", "desktop"
|
||||
],
|
||||
|
||||
fanTray: {
|
||||
price: 42,
|
||||
crateModesAvailable: {
|
||||
'rack': true
|
||||
},
|
||||
optionTitle: "Mount fan tray",
|
||||
tip: "Mount 84hp fan tray to the crate bottom for better air circulation"
|
||||
},
|
||||
|
||||
items: {
|
||||
/* keys are also ids, avoid changing them */
|
||||
'kasli': {
|
||||
@ -1153,6 +1162,7 @@ const shop_data = {
|
||||
"crates": [{
|
||||
id: "crate0",
|
||||
crate_mode: "rack",
|
||||
fan_tray: false,
|
||||
items: [],
|
||||
warnings: [],
|
||||
occupiedHP: 0,
|
||||
|
Loading…
Reference in New Issue
Block a user