'use strict'; import {create} from "zustand"; import {data as shared_data, itemsUnfoldedList} from "./utils"; import {true_type_of} from "./options/utils"; import {v4 as uuidv4} from "uuid"; import {FillResources} from "./count_resources"; import {TriggerCrateWarnings, TriggerWarnings} from "./warnings"; import {Validation, validateEmail, validateNote, validateJSONInput} from "./validate"; import {CratesToJSON, JSONToCrates} from "./json_porter"; const cards_to_pn_map = (cards) => { let result = {}; Object.entries(cards).forEach(([key, card], _i) => { result[card.name_number] = key}) return result; }; const useBacklog = ((set, get) => ({ cards: shared_data.items, groups: shared_data.columns.backlog, cards_list: itemsUnfoldedList, currency: shared_data.currency, pn_to_cards: cards_to_pn_map(shared_data.items), getCardDescription: index => get().cards[get().cards_list[index]], getCardDescriptionByPn: pn => get().cards[get().pn_to_cards[pn]], cardIndexById: card_id => get().cards_list.findIndex((element) => (card_id === element)) })); const useCrateModes = ((set, get) => ({ crate_modes: shared_data.crateModes, modes_order: shared_data.crateModeOrder, crateParams: mode => get().crate_modes[mode], })); const useLayout = ((set, get) => ({ isTouch: window.isTouchEnabled(), isMobile: window.deviceIsMobile(), sideMenuIsOpen: false, newCardJustAdded: false, switchSideMenu: () => set(state => ({ sideMenuIsOpen: !state.sideMenuIsOpen })), })); const useImportJSON = ((set, get) => ({ importShouldOpen: false, importValue: { value: "", error: Validation.OK }, openImport: () => set(state => ({ importShouldOpen: true })), closeImport: () => set(state => ({ importShouldOpen: false })), loadDescription: () => set(state => ({ importShouldOpen: false, crates: JSONToCrates(state.importValue.value) })), updateImportDescription: (new_description) => set(state => ({ importValue: { value: new_description, error: validateJSONInput(new_description) } })) })); const useSubmitForm = ((set, get) => ({ // TODO think about it isProcessing: false, shouldShowRFQFeedback: false, processingResult: { status: Validation.OK, message: "" }, API_RFQ: shared_data.API_RFQ, email: { value: "", error: null }, note: { value: "", error: Validation.OK }, description: "", shouldShowDescription: false, updateEmail: (new_email) => set(state => ({ email: { value: new_email, error: validateEmail(new_email) } })), updateNote: (new_notes) => set(state => ({ note: { value: new_notes, error: validateNote(new_notes) } })), _revalidateForm: () => set(state => ({ email: { value: state.email.value, error: validateEmail(state.email.value) }, note: { value: state.note.value, error: validateEmail(state.note.value) }, })), updateDescription: () => set(state => ({ description: CratesToJSON(state.crates) })), showDescription: () => set(state => ({ description: CratesToJSON(state.crates), shouldShowDescription: true })), closeDescription: () => set(state => ({ shouldShowDescription: false })), _submitForm: () => set(state => ({isProcessing: true})), finishSubmitForm: (result) => set(state => ({ isProcessing: false, shouldShowRFQFeedback: true, processingResult: result})), submitDisabled: () => (get().email.error !== Validation.OK), submitForm: () => { get().updateDescription(); get()._revalidateForm(); get()._submitForm(); if (get().submitDisabled()) return; fetch(get().API_RFQ, { method: "POST", headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ email: get().email.value, note: get().note.value, configuration: get().description }) }).then(response => { if (response.status !== 200) { throw Error("Response status is not OK: " + response.status + ".\n" + response); } get().finishSubmitForm({status: Validation.OK, message: "We've received your request and will be in contact soon."}) }).catch(err => { console.error("Request failed, reason:", err) get().finishSubmitForm({ status: Validation.Invalid, message: "We cannot receive your request. Try using the export by coping the configuration and send it to us at sales@m-labs.hk" }) }) }, closeRFQFeedback: () => set(state => ({shouldShowRFQFeedback: false})) })); 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 } })), })); 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: [], occupiedHP: 0 })})), delCrate: (id) => set(state => ({ crates: state.crates.filter((crate => crate.id !== id)) })), _setCrateMode: (id, mode) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate.id === id) { return { ...crate, crate_mode: mode } } else return crate; }) })), setActiveCrate: (id) => set(state => ({active_crate: id})), _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, _) => { return {...state.cards[card_name], id: uuidv4()} })) } } else return crate; }) } }), _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) => { if (crate_to === crate_from && crate_to === crate.id) { //const the_card = {...crate[index_from]}; let items_copy = Array.from(crate.items); delete items_copy[index_from]; return { ...crate, items: items_copy.toSpliced(index_to+1, 0, the_card).filter((item, _) => !!item) } } else if (crate_to === crate.id) { return { ...crate, items: crate.items.toSpliced(index_to, 0, the_card) } } else if (crate_from === crate.id) { return { ...crate, items: crate.items.toSpliced(index_to, 1) } } else return crate; }) } }), _deleteCard: (crate_id, index) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { return { ...crate, items: crate.items.toSpliced(index, 1) } } else return crate; }) })), _clearCrate: (id) => set(state => ({ crates: state.crates.map((crate, _i) => { if (id === crate.id) { return { ...crate, items: [] } } else return crate; }) })), clearAll: () => set(state => ({ crates: [] })), _updateOptions: (crate_id, index, new_options) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { let itemsCopy = Array.from(crate.items); itemsCopy[index].options_data = {...itemsCopy[index].options_data, ...new_options}; return { ...crate, items: itemsCopy } } else return crate; }) })), fillWarnings: (crate_id) => set(state => ({ crates: state.crates.map((crate, _i) => { if (crate_id === crate.id) { let itemsCopy = Array.from(crate.items); itemsCopy = FillResources(itemsCopy); itemsCopy = TriggerWarnings(itemsCopy); const [crate_warnings, occupied] = TriggerCrateWarnings(crate); return { ...crate, items: itemsCopy, 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) => { 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); } })) export const useShopStore = create((...params) => ({ ...useBacklog(...params), ...useCrateModes(...params), ...useCart(...params), ...useSubmitForm(...params), ...useLayout(...params), ...useHighlighted(...params), ...useImportJSON(...params), }))