From bc810355559f855580578d1da09284972ea86310 Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Mon, 29 Jan 2024 17:26:59 +0800 Subject: [PATCH 1/7] Prototype crate and order options Signed-off-by: Egor Savkin --- static/js/shop/options/Options.jsx | 19 ++++++++++ static/js/shop/shop_store.js | 27 ++++++++++---- static/js/shop_data.js | 58 ++++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/static/js/shop/options/Options.jsx b/static/js/shop/options/Options.jsx index 0993770..5fffb4f 100644 --- a/static/js/shop/options/Options.jsx +++ b/static/js/shop/options/Options.jsx @@ -41,3 +41,22 @@ export function ProcessOptions({options, data, target, id}) { } } +export function ProcessOptionsToData({options, data}) { + let options_t = true_type_of(options); + if (options_t === "array") { + return Array.from( + options.map((option_item, i) => ProcessOptionsToData({ + options: option_item, + data: data, + })) + ).flat(); + } else if (options_t === "object") { + if (true_type_of(options.title) === "string") { + return options; + } else { + return ProcessOptionsToData({options: json_logic_apply(options, data), data: data}); + } + } else { + throw Error("Incompatible type for the option: " + options_t) + } +} \ No newline at end of file diff --git a/static/js/shop/shop_store.js b/static/js/shop/shop_store.js index 1ad1f26..02f78ba 100644 --- a/static/js/shop/shop_store.js +++ b/static/js/shop/shop_store.js @@ -34,11 +34,25 @@ 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 useCrateOptions = ((set, get) => ({ + crate_options: shared_data.crateOptions.options, + crate_prices: shared_data.crateOptions.prices, + +})); + +const useOrderOptions = ((set, get) => ({ + orderOptions: shared_data.crateOptions.options, + orderPrices: shared_data.crateOptions.prices, + order_options_data: {}, + + fillOrderExtData: _ => {}, + + _updateOrderOption: (new_options) => set(state => ({ + order_options_data: { + ...state.order_options_data, + ...new_options + } + })) })); const useLayout = ((set, get) => ({ @@ -495,5 +509,6 @@ export const useShopStore = createWithEqualityFn((...params) => ({ ...useLayout(...params), ...useHighlighted(...params), ...useImportJSON(...params), - ...useFanTray(...params), + ...useCrateOptions(...params), + ...useOrderOptions(...params), })) \ No newline at end of file diff --git a/static/js/shop_data.js b/static/js/shop_data.js index 0b0b94c..9862564 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -28,13 +28,57 @@ const shop_data = { "rack", "desktop" ], - fanTray: { - price: 470, - crateModesAvailable: { - 'rack': true - }, - optionTitle: "Add fan tray", - tip: "Add 1U 84hp fan tray (to be mounted under the crate) to improve cooling. Fans need 220VAC 50/60Hz power. 3 fans, 167m³/h air flow." + crateOptions: { + options: [ + {"if": [ + {"==": [{"var": "ext_data.crate_mode"}, "rack",]}, + {type: "Switch", args: { + title: "Add fan tray", + outvar: "nuc", + tip: "Add 1U 84hp fan tray (to be mounted under the crate) to improve cooling. " + + "Fans need 220VAC 50/60Hz power. 3 fans, 167m³/h air flow.", + fallback: false, + }} + ]}, + ], + prices: [{"if": [{"and": [{"var": "fan_tray"}, {"==": [{"var": "ext_data.crate_mode"}, "rack",]}]}, {title: "Add fan tray", price: 470}]}] + }, + + orderOptions: { + options: [ + {type: "Switch", args: { + title: "Include optional pre-installed Intel® NUC mini-computer", + outvar: "nuc", + 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, " + + "32GB RAM from reputable vendor, 1TB M.2 NVM Express SSD from reputable vendor.", + fallback: true, + }}, + { + "if": [ + {"var": "nuc"}, + [ + { + type: "Radio", + args: { + title: "Desktop Environment", + outvar: "nuc_desktop", + variants: ["Gnome", "KDE"], + tip: "Gnome vs KDE", + fallback: 0 + } + }, + {type: "Line", args: {title: "Additional software to be pre-installed", outvar: "software", fallback: "", + tip: "Pre-install additional software, if needed"}}, + ], + ] + }, + ], + + prices: [{ + "if": [{"var": "nuc"}, {title: "Include optional pre-installed Intel® NUC mini-computer", price: 1300}, 0], + + }] }, items: { -- 2.42.0 From 4527189994c16c87b79b7e6cf64ca2d291449eea Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Tue, 30 Jan 2024 13:02:01 +0800 Subject: [PATCH 2/7] Add flexible crate options Signed-off-by: Egor Savkin --- static/js/shop/Cart.jsx | 3 - static/js/shop/Crate.jsx | 3 +- static/js/shop/CrateOptions.jsx | 43 ++++++++++++ static/js/shop/Shop.jsx | 2 + static/js/shop/SummaryCrate.jsx | 6 +- static/js/shop/SummaryCratePricedOptions.jsx | 48 ++++++++++++++ static/js/shop/options/Options.jsx | 5 +- static/js/shop/options/components/Switch.jsx | 9 +++ static/js/shop/options/utils.js | 8 ++- static/js/shop/shop_store.js | 69 ++++++++++++++++++-- static/js/shop_data.js | 8 ++- 11 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 static/js/shop/CrateOptions.jsx create mode 100644 static/js/shop/SummaryCratePricedOptions.jsx diff --git a/static/js/shop/Cart.jsx b/static/js/shop/Cart.jsx index fcc0aaa..1876a35 100644 --- a/static/js/shop/Cart.jsx +++ b/static/js/shop/Cart.jsx @@ -3,7 +3,6 @@ import {Droppable} from "@hello-pangea/dnd"; import {cartStyle, compareArraysWithIds} from "./utils"; import {ProductCartItem} from "./ProductCartItem"; import {FakePlaceholder} from "./FakePlaceholder"; -import {FillExtData} from "./options/utils"; import {hp_to_slots} from "./count_resources"; import {useShopStore} from "./shop_store"; @@ -29,12 +28,10 @@ export function Cart({crate_index}) { const nbrSlots = hp_to_slots(crateParams(crate.crate_mode).hp); const products = crate.items.map((item, index) => { - const ext_data = FillExtData(crate.items, index); return ( = nbrSlots} key={item.id}/> diff --git a/static/js/shop/Crate.jsx b/static/js/shop/Crate.jsx index bf2d837..432b866 100644 --- a/static/js/shop/Crate.jsx +++ b/static/js/shop/Crate.jsx @@ -7,6 +7,7 @@ import {useShopStore} from "./shop_store"; // #!render_count import {useRenderCount} from "@uidotdev/usehooks"; import {CrateFanTray} from "./CrateFanTray"; +import {CrateOptions} from "./CrateOptions"; /** @@ -45,7 +46,7 @@ export function Crate({crate_index}) { - + ); diff --git a/static/js/shop/CrateOptions.jsx b/static/js/shop/CrateOptions.jsx new file mode 100644 index 0000000..1380a30 --- /dev/null +++ b/static/js/shop/CrateOptions.jsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import {useShopStore} from "./shop_store"; +import {ProcessOptions, ProcessOptionsToData} from "./options/Options"; + +export function CrateOptions({crate_index}) { + const crate_id = useShopStore((state) => state.crates[crate_index].id); + const optionsLogic = useShopStore((state) => state.crate_options); + const updateOptions = useShopStore((state) => state.updateCrateOptions); + const options_data = useShopStore((state) => state.crates[crate_index].options_data || {}); + + console.log(options_data) + + const options = ProcessOptions({ + options: optionsLogic, + data: options_data, + id: "crate_options" + crate_id, + target: { + construct: ((outvar, value) => { + // #!options_log + console.log("construct", outvar, value, options_data); + + options_data[outvar] = value; + }), + update: ((outvar, value) => { + // #!options_log + console.log("update", outvar, value, options_data); + + if (outvar in options_data) options_data[outvar] = value; + + updateOptions(crate_id, {[outvar]: value}); + }) + } + }); + + console.log(options) + + return ( +
+ {options} +
+ ) +} \ No newline at end of file diff --git a/static/js/shop/Shop.jsx b/static/js/shop/Shop.jsx index 7e5e63e..6ea7582 100644 --- a/static/js/shop/Shop.jsx +++ b/static/js/shop/Shop.jsx @@ -19,6 +19,7 @@ export function Shop() { const renderCount = useRenderCount(); const addCardFromBacklog = useShopStore((state) => state.addCardFromBacklog); + const initExtData = useShopStore((state) => state.initExtData); const moveCard = useShopStore((state) => state.moveCard); const deleteCard = useShopStore((state) => state.deleteCard); const cardIndexById = useShopStore((state) => state.cardIndexById); @@ -38,6 +39,7 @@ export function Shop() { useEffect(() => { addCardFromBacklog(null, [cardIndexById("eem_pwr_mod"), cardIndexById("kasli")], -1, true); + initExtData(); }, []); // #!render_count diff --git a/static/js/shop/SummaryCrate.jsx b/static/js/shop/SummaryCrate.jsx index e739ee2..caa2abf 100644 --- a/static/js/shop/SummaryCrate.jsx +++ b/static/js/shop/SummaryCrate.jsx @@ -3,10 +3,11 @@ import React from "react"; import {useShopStore} from "./shop_store"; import {SummaryCrateHeader} from "./SummaryCrateHeader"; import {SummaryCrateCard} from "./SummaryCrateCard"; +import {SummaryCratePricedOptions} from "./SummaryCratePricedOptions"; // #!render_count import {useRenderCount} from "@uidotdev/usehooks"; -import {SummaryCrateFanTray} from "./SummaryCrateFanTray"; + export function SummaryCrate({crate_index}) { // #!render_count @@ -26,7 +27,8 @@ export function SummaryCrate({crate_index}) { {range(0, crate_len).map((index, _i) => )} - + + ) } \ No newline at end of file diff --git a/static/js/shop/SummaryCratePricedOptions.jsx b/static/js/shop/SummaryCratePricedOptions.jsx new file mode 100644 index 0000000..f779e28 --- /dev/null +++ b/static/js/shop/SummaryCratePricedOptions.jsx @@ -0,0 +1,48 @@ +import {formatMoney} from "./utils"; +import React from "react"; +import {useShopStore} from "./shop_store"; +import {ProcessOptionsToData} from "./options/Options"; + +// #!render_count +import {useRenderCount} from "@uidotdev/usehooks"; + +export function SummaryCratePricedOptions({crate_index}) { + // #!render_count + const renderCount = useRenderCount(); + + const currency = useShopStore((state) => state.currency); + const crate_id = useShopStore((state) => state.crates[crate_index].id); + const optionsPrices = useShopStore((state) => state.crate_prices); + const updateOptions = useShopStore((state) => state.updateCrateOptions); + const options_data = useShopStore((state) => state.crates[crate_index].options_data || {}); + + const options = ProcessOptionsToData({options: optionsPrices, data: options_data}); + + console.log(options, options_data, optionsPrices) + + // #!render_count + console.log("SummaryCratePricedOptions renders: ", renderCount) + + return options.map((option, i) => ( + + +   +
{option.title}
+ + + +
+ {`${currency} ${formatMoney(option.price)}`} + + + +
+
+ + + )); +} \ No newline at end of file diff --git a/static/js/shop/options/Options.jsx b/static/js/shop/options/Options.jsx index 5fffb4f..a0c68ac 100644 --- a/static/js/shop/options/Options.jsx +++ b/static/js/shop/options/Options.jsx @@ -49,7 +49,7 @@ export function ProcessOptionsToData({options, data}) { options: option_item, data: data, })) - ).flat(); + ).filter((item, i) => !!item).flat(); } else if (options_t === "object") { if (true_type_of(options.title) === "string") { return options; @@ -57,6 +57,7 @@ export function ProcessOptionsToData({options, data}) { return ProcessOptionsToData({options: json_logic_apply(options, data), data: data}); } } else { - throw Error("Incompatible type for the option: " + options_t) + //throw Error("Incompatible type for the option: " + options_t) + return null; } } \ No newline at end of file diff --git a/static/js/shop/options/components/Switch.jsx b/static/js/shop/options/components/Switch.jsx index 64e5d4c..44cbe54 100644 --- a/static/js/shop/options/components/Switch.jsx +++ b/static/js/shop/options/components/Switch.jsx @@ -23,6 +23,15 @@ class Switch extends Component { this.props.target.update(this.props.outvar, new_checked); } + static getDerivedStateFromProps(props, current_state) { + if (current_state.checked !== props.data[props.outvar]) { + return { + checked: props.data[props.outvar] + } + } + return null + } + render() { let key = this.props.id + this.props.outvar; return ( diff --git a/static/js/shop/options/utils.js b/static/js/shop/options/utils.js index 79e0670..56288ae 100644 --- a/static/js/shop/options/utils.js +++ b/static/js/shop/options/utils.js @@ -4,7 +4,7 @@ import {componentsList} from "./components/components"; // https://stackoverflow.com/a/70511311 export const true_type_of = (obj) => Object.prototype.toString.call(obj).slice(8, -1).toLowerCase(); -export function FillExtData(data, index) { +export function FillExtCardData(data, index) { return { has_other_dio: data.filter((value, item_index) => index !== item_index && value.name &&value.name.endsWith("-TTL")).length > 0, has_dds: data.filter(((value, _) => value.name === "DDS" && value.name_number === "4410" && (!value.options_data || !value.options_data.mono_eem))).length > 0, @@ -12,6 +12,12 @@ export function FillExtData(data, index) { } } +export function FillExtCrateData(crate) { + return { + crate_mode: crate.crate_mode + } +} + export function FilterOptions(options, data) { let options_t = true_type_of(options); let target = {}; diff --git a/static/js/shop/shop_store.js b/static/js/shop/shop_store.js index 02f78ba..7900155 100644 --- a/static/js/shop/shop_store.js +++ b/static/js/shop/shop_store.js @@ -2,10 +2,10 @@ import {createWithEqualityFn} from "zustand/traditional"; import {data as shared_data, itemsUnfoldedList} from "./utils"; -import {true_type_of} from "./options/utils"; +import {FillExtCrateData, true_type_of} from "./options/utils"; import {v4 as uuidv4} from "uuid"; import {FillResources} from "./count_resources"; -import {FillExtData} from "./options/utils"; +import {FillExtCardData} from "./options/utils"; import {TriggerCrateWarnings, TriggerWarnings} from "./warnings"; import {Validation, validateEmail, validateNote, validateJSONInput} from "./validate"; import {CratesToJSON, JSONToCrates} from "./json_porter"; @@ -38,6 +38,49 @@ const useCrateOptions = ((set, get) => ({ crate_options: shared_data.crateOptions.options, crate_prices: shared_data.crateOptions.prices, + fillExtCrateData: (crate_id) => set(state => ({ + crates: state.crates.map((crate, _i) => { + if (crate_id === crate.id) { + const previous_options = crate.options_data || {}; + return { + ...crate, + options_data: { + ...previous_options, + ext_data: FillExtCrateData(crate) + } + } + } + else return crate; + }) + })), + + _updateCrateOption: (crate_id, new_options) => set(state => ({ + crates: state.crates.map((crate, _i) => { + if (crate_id === crate.id) { + const previous_options = crate.options_data || {}; + console.log(crate_id, new_options, { + ...crate, + options_data: { + ...previous_options, + ...new_options + } + }) + return { + ...crate, + options_data: { + ...previous_options, + ...new_options + } + } + } + else return crate; + }) + })), + + updateCrateOptions: (crate_id, new_options) => { + get().fillExtCrateData(crate_id); + get()._updateCrateOption(crate_id, new_options); + } })); const useOrderOptions = ((set, get) => ({ @@ -45,14 +88,20 @@ const useOrderOptions = ((set, get) => ({ orderPrices: shared_data.crateOptions.prices, order_options_data: {}, + // in case of future needs fillOrderExtData: _ => {}, - _updateOrderOption: (new_options) => set(state => ({ + _updateOrderOptions: (new_options) => set(state => ({ order_options_data: { ...state.order_options_data, ...new_options } - })) + })), + + updateOrderOptions: (new_options) => { + get().fillOrderExtData(); + get()._updateOrderOptions(new_options); + } })); const useLayout = ((set, get) => ({ @@ -400,7 +449,7 @@ const useCart = ((set, get) => ({ itemsCopy = itemsCopy.map((item, index) => { if (!item.options) return item; if (!item.options_data) item.options_data = {}; - item.options_data.ext_data = FillExtData(itemsCopy, index); + item.options_data.ext_data = FillExtCardData(itemsCopy, index); return item; }); return { @@ -430,12 +479,14 @@ const useCart = ((set, get) => ({ const crate_id = "crate" + get().crates.length; get()._newCrate(crate_id) get().fillExtData(crate_id); + get().fillExtCrateData(crate_id); get().fillWarnings(crate_id); }, setCrateMode: (id, mode) => { get()._setCrateMode(id, mode) get().fillExtData(id); + get().fillExtCrateData(id); get().fillWarnings(id); get().setActiveCrate(id); }, @@ -497,6 +548,14 @@ const useCart = ((set, get) => ({ fanTrayAvailableByIndex: (crate_index) => { return get().fanTrayAvailableForMode(get().crates[crate_index].crate_mode); + }, + + initExtData: () => { + get().fillOrderExtData(); + get().crates.forEach((crate, _i) => { + get().fillExtData(crate.id); + get().fillExtCrateData(crate.id); + }) } })) diff --git a/static/js/shop_data.js b/static/js/shop_data.js index 9862564..9660093 100644 --- a/static/js/shop_data.js +++ b/static/js/shop_data.js @@ -34,14 +34,16 @@ const shop_data = { {"==": [{"var": "ext_data.crate_mode"}, "rack",]}, {type: "Switch", args: { title: "Add fan tray", - outvar: "nuc", + outvar: "fan_tray", tip: "Add 1U 84hp fan tray (to be mounted under the crate) to improve cooling. " + "Fans need 220VAC 50/60Hz power. 3 fans, 167m³/h air flow.", - fallback: false, + fallback: false }} ]}, ], - prices: [{"if": [{"and": [{"var": "fan_tray"}, {"==": [{"var": "ext_data.crate_mode"}, "rack",]}]}, {title: "Add fan tray", price: 470}]}] + prices: [{"if": [ + {"and": [{"var": "fan_tray"}, {"==": [{"var": "ext_data.crate_mode"}, "rack",]}]}, + {title: "Add fan tray", price: 470, disable_patch: {"fan_tray": false}, id: "fan_tray"}]}] }, orderOptions: { -- 2.42.0 From 15d91240253fbf2ad928b100d48159edcebe7a95 Mon Sep 17 00:00:00 2001 From: Egor Savkin Date: Tue, 30 Jan 2024 14:45:30 +0800 Subject: [PATCH 3/7] Cache total price calculation Signed-off-by: Egor Savkin --- static/js/shop.bundle.js | 61763 ++++++++++++++++- static/js/shop/Crate.jsx | 3 +- static/js/shop/CrateFanTray.jsx | 37 - static/js/shop/CrateOptions.jsx | 4 - static/js/shop/SummaryCrateFanTray.jsx | 45 - static/js/shop/SummaryCratePricedOptions.jsx | 2 - static/js/shop/SummaryTotalPrice.jsx | 2 +- static/js/shop/json_porter.js | 7 +- static/js/shop/shop_store.js | 51 +- 9 files changed, 61787 insertions(+), 127 deletions(-) delete mode 100644 static/js/shop/CrateFanTray.jsx delete mode 100644 static/js/shop/SummaryCrateFanTray.jsx diff --git a/static/js/shop.bundle.js b/static/js/shop.bundle.js index 18d3538..2db5e76 100644 --- a/static/js/shop.bundle.js +++ b/static/js/shop.bundle.js @@ -1,2 +1,61761 @@ -/*! For license information please see shop.bundle.js.LICENSE.txt */ -(()=>{var e={841:(e,t)=>{"use strict";var n=Symbol.for("react.element"),r=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.provider"),s=Symbol.for("react.context"),c=Symbol.for("react.server_context"),u=Symbol.for("react.forward_ref"),d=Symbol.for("react.suspense"),f=Symbol.for("react.suspense_list"),p=Symbol.for("react.memo"),m=Symbol.for("react.lazy");Symbol.for("react.offscreen");Symbol.for("react.module.reference"),t.isContextConsumer=function(e){return function(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case n:switch(e=e.type){case o:case i:case a:case d:case f:return e;default:switch(e=e&&e.$$typeof){case c:case s:case u:case m:case p:case l:return e;default:return t}}case r:return t}}}(e)===s}},366:(e,t,n)=>{"use strict";e.exports=n(841)},184:(e,t)=>{var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],t=0;t{"use strict";var r=n(296),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},a={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||o}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var o=p(n);o&&o!==m&&e(t,o,r)}var i=u(n);d&&(i=i.concat(d(n)));for(var l=s(t),g=s(n),h=0;h{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,o=n?Symbol.for("react.portal"):60106,a=n?Symbol.for("react.fragment"):60107,i=n?Symbol.for("react.strict_mode"):60108,l=n?Symbol.for("react.profiler"):60114,s=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,u=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,f=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,m=n?Symbol.for("react.suspense_list"):60120,g=n?Symbol.for("react.memo"):60115,h=n?Symbol.for("react.lazy"):60116,y=n?Symbol.for("react.block"):60121,b=n?Symbol.for("react.fundamental"):60117,v=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function E(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case u:case d:case a:case l:case i:case p:return e;default:switch(e=e&&e.$$typeof){case c:case f:case h:case g:case s:return e;default:return t}}case o:return t}}}function x(e){return E(e)===d}t.AsyncMode=u,t.ConcurrentMode=d,t.ContextConsumer=c,t.ContextProvider=s,t.Element=r,t.ForwardRef=f,t.Fragment=a,t.Lazy=h,t.Memo=g,t.Portal=o,t.Profiler=l,t.StrictMode=i,t.Suspense=p,t.isAsyncMode=function(e){return x(e)||E(e)===u},t.isConcurrentMode=x,t.isContextConsumer=function(e){return E(e)===c},t.isContextProvider=function(e){return E(e)===s},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return E(e)===f},t.isFragment=function(e){return E(e)===a},t.isLazy=function(e){return E(e)===h},t.isMemo=function(e){return E(e)===g},t.isPortal=function(e){return E(e)===o},t.isProfiler=function(e){return E(e)===l},t.isStrictMode=function(e){return E(e)===i},t.isSuspense=function(e){return E(e)===p},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===a||e===d||e===l||e===i||e===p||e===m||"object"==typeof e&&null!==e&&(e.$$typeof===h||e.$$typeof===g||e.$$typeof===s||e.$$typeof===c||e.$$typeof===f||e.$$typeof===b||e.$$typeof===v||e.$$typeof===w||e.$$typeof===y)},t.typeOf=E},296:(e,t,n)=>{"use strict";e.exports=n(103)},143:e=>{"use strict";e.exports=function(e,t,n,r,o,a,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,o,a,i,l],u=0;(s=new Error(t.replace(/%s/g,(function(){return c[u++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},962:function(e,t,n){var r,o;r=function(){"use strict";Array.isArray||(Array.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)});var e={},t={"==":function(e,t){return e==t},"===":function(e,t){return e===t},"!=":function(e,t){return e!=t},"!==":function(e,t){return e!==t},">":function(e,t){return e>t},">=":function(e,t){return e>=t},"<":function(e,t,n){return void 0===n?e=t?[]:r}};return e.is_logic=function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)&&1===Object.keys(e).length},e.truthy=function(e){return!(Array.isArray(e)&&0===e.length||!e)},e.get_operator=function(e){return Object.keys(e)[0]},e.get_values=function(t){return t[e.get_operator(t)]},e.apply=function(n,r){if(Array.isArray(n))return n.map((function(t){return e.apply(t,r)}));if(!e.is_logic(n))return n;var o,a,i,l,s,c=e.get_operator(n),u=n[c];if(Array.isArray(u)||(u=[u]),"if"===c||"?:"==c){for(o=0;o0){var d=String(c).split("."),f=t;for(o=0;o{"use strict";var r=n(414);function o(){}function a(){}a.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,a,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:a,resetWarningCache:o};return n.PropTypes=n,n}},697:(e,t,n)=>{e.exports=n(703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},448:(e,t,n)=>{"use strict";var r=n(294),o=n(840);function a(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n