Add css classes to the radio component, add shipping summary and other content fixes

Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
Egor Savkin 2024-02-02 17:24:01 +08:00
parent 0b5797b1ba
commit 759f7cffcc
13 changed files with 592 additions and 469 deletions

912
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,24 +13,23 @@
"url": "https://git.m-labs.hk/M-Labs/web2019.git" "url": "https://git.m-labs.hk/M-Labs/web2019.git"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.23.0", "@babel/cli": "^7.23.9",
"@babel/core": "^7.23.2", "@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.2", "@babel/preset-env": "^7.23.9",
"@babel/preset-react": "^7.22.15", "@babel/preset-react": "^7.23.3",
"babel-loader": "^9.1.3", "babel-loader": "^9.1.3",
"babel-preset-minify": "^0.5.2", "babel-preset-minify": "^0.5.2",
"bootstrap": "^5.3.0", "bootstrap": "^5.3.2",
"jquery": "^3.7.0", "jquery": "^3.7.1",
"prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.10.0",
"@hello-pangea/dnd": "^16.5.0", "@hello-pangea/dnd": "^16.5.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"webpack": "^5.89.0", "webpack": "^5.90.1",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"json-logic-js": "^2.0.2", "json-logic-js": "^2.0.2",
"zustand": "^4.4.7", "zustand": "^4.5.0",
"@uidotdev/usehooks":"^2.4.1", "@uidotdev/usehooks":"^2.4.1",
"webpack-preprocessor-loader": "^1.3.0" "webpack-preprocessor-loader": "^1.3.0"
}, },

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@ import {SummaryCrate} from "./SummaryCrate";
// #!render_count // #!render_count
import {useRenderCount} from "@uidotdev/usehooks"; import {useRenderCount} from "@uidotdev/usehooks";
import {SummaryOrderPricedOptions} from "./SummaryOrderPricedOptions"; import {SummaryOrderPricedOptions} from "./SummaryOrderPricedOptions";
import {SummaryOrderShipping} from "./SummaryOrderShipping";
export function SummaryCrates() { export function SummaryCrates() {
// #!render_count // #!render_count
@ -22,6 +23,7 @@ export function SummaryCrates() {
return <SummaryCrate crate_index={index} key={"summary_crate_body_" + index} /> return <SummaryCrate crate_index={index} key={"summary_crate_body_" + index} />
})} })}
<SummaryOrderPricedOptions/> <SummaryOrderPricedOptions/>
<SummaryOrderShipping/>
</> </>
) )
} }

View File

@ -0,0 +1,35 @@
import {formatMoney} from "./utils";
import React from "react";
import {useShopStore} from "./shop_store";
import {ProcessOptions, ProcessOptionsToData} from "./options/Options";
// #!render_count
import {useRenderCount} from "@uidotdev/usehooks";
export function SummaryOrderShipping() {
// #!render_count
const renderCount = useRenderCount();
const shipping_summary = useShopStore((state) => state.shipping_summary);
const options_data = useShopStore((state) => state.order_options_data);
// #!render_count
console.log("SummaryOrderShipping renders: ", renderCount)
const options = ProcessOptions({
options: shipping_summary,
data: options_data,
id: "shipping_options",
target: null,
});
return <tbody key="summary_shipping_order_body">
{options.map((option, i) => (
<tr key={"summary_shipping_order_option_" + i} id={"summary_shipping_order_option_" + i}>
<td className="item-card-name" key={"summary_shipping_order_key_option_" + i} id={"summary_shipping_order_key_option_" + i}>
{option}
</td>
</tr>
))}
</tbody>;
}

View File

@ -0,0 +1,17 @@
import React from "react";
import {apply as json_logic_apply, add_operation as json_add_operation} from "json-logic-js";
json_add_operation("lower", (some_str) => some_str.toLowerCase(some_str));
json_add_operation("upper", (some_str) => some_str.toUpperCase(some_str));
json_add_operation("capitalize", (some_str) => some_str.capitalizeFirstLetter(some_str));
export function Label(target, id, data, {content}) {
const resulting_string = json_logic_apply(content, data).flat().join("");
return (
<div id={id+"label"} key={id+"label"} className="options-label">
{resulting_string}
</div>
)
}

View File

@ -46,7 +46,7 @@ class Line extends Component {
} }
} }
export function LineWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { export function LineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes}) {
return <Line target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id} return <Line target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id}
id={id} data={data}/>; id={id} data={data} classes={classes}/>;
} }

View File

@ -42,7 +42,7 @@ class Radio extends Component {
</div> </div>
{this.props.tip && <Tip id={this.props.id + "tooltip"} tip={this.props.tip}/>} {this.props.tip && <Tip id={this.props.id + "tooltip"} tip={this.props.tip}/>}
{this.props.variants.map((variant, _) => ( {this.props.variants.map((variant, _) => (
<div className="form-check shop-radio-variant" key={key + variant}> <div className={`form-check shop-radio-variant ${this.props.classes}`} key={key + variant}>
<input <input
className="form-check-input" className="form-check-input"
type="radio" type="radio"
@ -62,8 +62,8 @@ class Radio extends Component {
} }
} }
export function RadioWrapper(target, id, data, {title, variants, outvar, fallback, icon, tip}) { export function RadioWrapper(target, id, data, {title, variants, outvar, fallback, icon, tip, classes}) {
return <Radio target={target} title={title} variants={variants} outvar={outvar} icon={icon} tip={tip} key={id} return <Radio target={target} title={title} variants={variants} outvar={outvar} icon={icon} tip={tip} key={id}
fallback={fallback} fallback={fallback} classes={classes}
id={id} data={data}/>; id={id} data={data}/>;
} }

View File

@ -57,7 +57,7 @@ class Switch extends Component {
} }
} }
export function SwitchWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { export function SwitchWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes}) {
return <Switch target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id} return <Switch target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id}
id={id} data={data}/>; id={id} data={data} classes={classes}/>;
} }

View File

@ -71,7 +71,7 @@ class SwitchLine extends Component {
} }
} }
export function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon, tip}) { export function SwitchLineWrapper(target, id, data, {title, fallback, outvar, icon, tip, classes}) {
return <SwitchLine target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id} return <SwitchLine target={target} title={title} fallback={fallback} outvar={outvar} icon={icon} tip={tip} key={id}
id={id} data={data}/>; id={id} data={data} classes={classes}/>;
} }

View File

@ -5,6 +5,7 @@ import {RadioWrapper} from "./Radio";
import {SwitchWrapper} from "./Switch"; import {SwitchWrapper} from "./Switch";
import {SwitchLineWrapper} from "./SwitchLine"; import {SwitchLineWrapper} from "./SwitchLine";
import {UnimplementedComponent} from "./UnimplementedComponent"; import {UnimplementedComponent} from "./UnimplementedComponent";
import {Label} from "./Label";
// Class components are used because we cannot use hooks for updating the state // Class components are used because we cannot use hooks for updating the state
@ -13,5 +14,6 @@ export const componentsList = {
"Switch": SwitchWrapper, "Switch": SwitchWrapper,
"Line": LineWrapper, "Line": LineWrapper,
"SwitchLine": SwitchLineWrapper, "SwitchLine": SwitchLineWrapper,
"Label": Label,
"Default": UnimplementedComponent, "Default": UnimplementedComponent,
}; };

View File

@ -82,6 +82,7 @@ const useCrateOptions = ((set, get) => ({
const useOrderOptions = ((set, get) => ({ const useOrderOptions = ((set, get) => ({
order_options: shared_data.orderOptions.options, order_options: shared_data.orderOptions.options,
order_prices: shared_data.orderOptions.prices, order_prices: shared_data.orderOptions.prices,
shipping_summary: shared_data.orderOptions.shippingSummary,
order_options_data: {}, order_options_data: {},
fillOrderExtData: () => set(state => ({ fillOrderExtData: () => set(state => ({

View File

@ -50,7 +50,7 @@ const shop_data = {
options: [ options: [
{"type": "Group", items:[ {"type": "Group", items:[
{type: "Switch", args: { {type: "Switch", args: {
title: "Include optional pre-installed Intel® NUC mini-computer", title: "Include optional Intel® NUC mini-computer with pre-installed ARTIQ software",
outvar: "nuc", outvar: "nuc",
tip: "OS: latest stable NixOS with Gnome or KDE with pre-installed ARTIQ software. " + tip: "OS: latest stable NixOS with Gnome or KDE with pre-installed ARTIQ software. " +
"Hardware (other choices available): Intel® NUC 13 Pro Kit NUC13ANKi7, i7-1360P CPU, " + "Hardware (other choices available): Intel® NUC 13 Pro Kit NUC13ANKi7, i7-1360P CPU, " +
@ -68,19 +68,29 @@ const shop_data = {
outvar: "nuc_desktop", outvar: "nuc_desktop",
variants: ["Gnome", "KDE"], variants: ["Gnome", "KDE"],
tip: "Gnome has clean and minimalist design. KDE has more feature-rich and classic interface.", tip: "Gnome has clean and minimalist design. KDE has more feature-rich and classic interface.",
fallback: 0 fallback: 0,
classes: "form-check-inline ms-4"
} }
}, },
{type: "Line", args: {title: "Additional software to be pre-installed", outvar: "software", fallback: "", {type: "Line", args: {title: "Additional software to be pre-installed", outvar: "software", fallback: "",
tip: "Pre-install additional software, if needed."}}, tip: "Pre-install additional software, if needed."}},
{"if": [
{"var": "ext_data.has_crate"},
{type: "Switch", args: {
title: "Include promotional USB stick",
outvar: "include_usb_stick_nuc",
tip: "Choose if you need a USB stick and wish to receive configuration files via other electronic means (e.g. email or cloud).",
fallback: false,
}}
]},
], ],
{"if": [ {"if": [
{"var": "ext_data.has_crate"}, {"var": "ext_data.has_crate"},
{type: "Switch", args: { {type: "Switch", args: {
title: "Opt-out from promotional USB stick", title: "Include promotional USB stick",
outvar: "usb_stick_opt_out", outvar: "include_usb_stick",
tip: "Choose if you don't need a USB stick and wish to receive configuration files via other electronic means (e.g. email or cloud).", tip: "Choose if you need a USB stick and wish to receive configuration files via other electronic means (e.g. email or cloud).",
fallback: false, fallback: true,
}} }}
]}, ]},
] ]
@ -127,7 +137,32 @@ const shop_data = {
prices: [{ prices: [{
"if": [{"var": "nuc"}, {title: "Include optional pre-installed Intel® NUC mini-computer", price: 1300, disable_patch: {"nuc": false}, id: "nuc"}], "if": [{"var": "nuc"}, {title: "Include optional pre-installed Intel® NUC mini-computer", price: 1300, disable_patch: {"nuc": false}, id: "nuc"}],
}] }],
shippingSummary: [
{type: "Label", args: {
content: ["Shipping method: ", {"var": "shipping"}]
}},
{"if": [
{"var": "shipping_instructions"},
{type: "Label", args: {
content: [
{"if": [
{"==": [{"var": "shipping"}, "Incoterms 2020 FCA"]},
"carrier account information and/or other shipping instructions: ",
"delivery address: "
]},
{"var": "shipping_instructions"}
]
}}]},
{type: "Label", args: {
content: [
{"if": [
{"==": [{"var": "shipping"}, "Prepay and add shipping (only available to credit customers)"]},
["In case of additional customs fees: ", {"lower": {"var": "prepay_fees_handling"}}],
]}
]
}},
]
}, },
items: { items: {