import produce from 'immer' import _ from 'lodash' import ActionTypes from './ActionTypes' export const initBatchHistory: IBatchHistories = { preBatchHistories: [], pastBatchHistories: [], } export const initialState: IOrderLibState = { // * Menu rootCategory: null, rootSetCategory: null, categories: {}, menus: {}, sets: {}, selectedCategory: null, selectedSet: null, selectedSetStep: null, // * Order selectedOrder: null, // * Batch selectedBatch: null, selectedItemKey: null, selectedSetItemKey: null, // * Batch (other) menuItemQuantities: {}, batchStashes: [], batchHistory: initBatchHistory, } function createBatchHistory (draft: ILibOrderBatchState, itemKey?: string, setItemKey?: string): IBatchHistory { return { selectedItemKey: itemKey === undefined ? draft.selectedItemKey : itemKey, selectedSetItemKey: setItemKey === undefined ? draft.selectedSetItemKey : setItemKey, batch: _.cloneDeep(draft.selectedBatch), menuItemQuantities: _.cloneDeep(draft.menuItemQuantities), createdAt: new Date(), } } function storeHistory (draft: ILibOrderBatchState, noHistory: boolean = false): void { // 不紀錄歷史 if (noHistory) return // 記錄歷史 const history = createBatchHistory(draft) draft.batchHistory.preBatchHistories.push(history) // 清除 REDO draft.batchHistory.pastBatchHistories = [] } export default produce( (draft: ILibOrderBatchState, action: IAction) => { switch (action?.type) { case 'LOGOUT': { return initialState } case ActionTypes.SHOW_NOT_COMPLETE_SETS: { const { showNotCompleteSets } = action.payload as {showNotCompleteSets: boolean} draft.selectedBatch.showNotCompleteSets = showNotCompleteSets break } case ActionTypes.UNDO_BATCH: { // 將現在狀態放入 pastBatchHistory 的第一個 const history = createBatchHistory(draft) draft.batchHistory.pastBatchHistories.unshift(history) // 拿出最後一個 preBatchHistories,並用覆寫目前的 batch const preBatchHistory = draft.batchHistory.preBatchHistories.pop() draft.selectedBatch = _.cloneDeep(preBatchHistory.batch) draft.menuItemQuantities = _.cloneDeep(preBatchHistory.menuItemQuantities) break } case ActionTypes.REDO_BATCH: { // 將現在狀態放入 preBatchHistories 的最後 const history = createBatchHistory(draft) draft.batchHistory.preBatchHistories.push(history) // 拿出第一個 pastBatchHistories batch const pastBatchHistory = draft.batchHistory.pastBatchHistories.shift() draft.selectedBatch = _.cloneDeep(pastBatchHistory.batch) draft.menuItemQuantities = _.cloneDeep(pastBatchHistory.menuItemQuantities) break } case ActionTypes.SELECT_BATCH: { const { batch } = action.payload as {batch: IAppOrderBatch} // reset batch history draft.batchHistory = initBatchHistory draft.selectedBatch = batch break } case ActionTypes.ADD_ITEM: { const { item, noHistory } = action.payload as { item: IAppBatchItem, noHistory: boolean } storeHistory(draft, noHistory) // 新增項目 draft.selectedBatch.items.push(item) // 記錄餐點數量 const originalQuantity = Number.isNaN(draft.menuItemQuantities[item.menuId]) ? 0 : draft.menuItemQuantities[item.menuId] draft.menuItemQuantities[item.menuId] = originalQuantity + item.quantity break } case ActionTypes.ADD_SET_ITEM: { const { item, noHistory } = action.payload as { item: IAppBatchItem, noHistory: boolean } const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return storeHistory(draft, noHistory) // 新增項目 selectedItem.setItems.push(item) // 記錄餐點數量 const originalQuantity = Number.isNaN(draft.menuItemQuantities[item.menuId]) ? 0 : draft.menuItemQuantities[item.menuId] draft.menuItemQuantities[item.menuId] = originalQuantity + item.quantity break } case ActionTypes.SELECT_ITEM: { const { itemKey } = action.payload as {itemKey: TItemKey} draft.selectedItemKey = itemKey break } case ActionTypes.SELECT_SET_ITEM: { const { setItemKey } = action.payload as {setItemKey: TSetItemKey} draft.selectedSetItemKey = setItemKey break } case ActionTypes.UPDATE_ITEM: { const { path, value, noHistory } = action.payload as {path: PropertyPath, value: any, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return storeHistory(draft, noHistory) // 如果是更改數量,記錄餐點數量 if (path === 'quantity' || (Array.isArray(path) && path[0] === 'quantity')) { const originalQuantity = _.get(selectedItem, path, 0) draft.menuItemQuantities[selectedItem.menuId] -= originalQuantity draft.menuItemQuantities[selectedItem.menuId] += value as number } // 更改項目 _.set(selectedItem, path, value) break } case ActionTypes.UPDATE_SET_ITEM: { const { path, value, noHistory } = action.payload as {path: PropertyPath, value: any, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems.find(setItem => setItem.key === draft.selectedSetItemKey) if (selectedSetItem == null) return storeHistory(draft, noHistory) // 如果是更改數量,記錄餐點數量 if (path === 'quantity' || (Array.isArray(path) && path[0] === 'quantity')) { const originalQuantity = _.get(selectedSetItem, path) draft.menuItemQuantities[selectedSetItem.menuId] -= originalQuantity draft.menuItemQuantities[selectedSetItem.menuId] += value as number } // 更改項目 _.set(selectedSetItem, path, value) break } case ActionTypes.RESET_ITEMS: { storeHistory(draft) // 清除所有 items draft.selectedBatch.items = [] // 清除餐點數量 draft.menuItemQuantities = {} break } case ActionTypes.DELETE_ITEM: { const { index, noHistory } = action.payload as {index: number, noHistory: boolean} const selectedItem = draft.selectedBatch.items[index] storeHistory(draft, noHistory) // 扣除餐點數量 selectedItem.setItems.forEach(setItem => { draft.menuItemQuantities[setItem.menuId] -= setItem.quantity }) draft.menuItemQuantities[selectedItem.menuId] -= selectedItem.quantity // 刪除 draft.selectedBatch.items.splice(index, 1) break } case ActionTypes.DELETE_SET_ITEM: { const { index, noHistory } = action.payload as {index: number, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems[index] storeHistory(draft, noHistory) // 扣除餐點數量 draft.menuItemQuantities[selectedSetItem.menuId] -= selectedSetItem.quantity // 刪除 selectedItem.setItems.splice(index, 1) break } case ActionTypes.ADD_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return storeHistory(draft, noHistory) // 新增 option selectedItem.options.push(option) break } case ActionTypes.ADD_SET_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems.find(setItem => setItem.key === draft.selectedSetItemKey) if (selectedSetItem == null) return storeHistory(draft, noHistory) // 新增 option selectedSetItem.options.push(option) break } case ActionTypes.UPDATE_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedItemOptionIndex = selectedItem.options.findIndex(selectedItemOption => selectedItemOption.optionGroupId === option.optionGroupId) storeHistory(draft, noHistory) // 修改 option if (selectedItemOptionIndex >= 0) { selectedItem.options[selectedItemOptionIndex] = option } else { selectedItem.options.push(option) } break } case ActionTypes.TOGGLE_ITEM_TAG: { const { tag, noHistory } = action.payload as {tag: IMenuTag, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return storeHistory(draft, noHistory) // 修改 tags const itemTagIndex = selectedItem.tags.findIndex(itemTag => itemTag.name === tag.name) if (itemTagIndex > -1) { selectedItem.tags.splice(itemTagIndex, 1) } else { selectedItem.tags.push(tag) } break } case ActionTypes.UPDATE_SET_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems.find(setItem => setItem.key === draft.selectedSetItemKey) if (selectedSetItem == null) return const selectedSetItemOptionIndex = selectedSetItem.options.findIndex(selectedSetItemOption => selectedSetItemOption.optionItemId === option.optionItemId) storeHistory(draft, noHistory) // 修改 option if (selectedSetItemOptionIndex >= 0) { selectedSetItem.options[selectedSetItemOptionIndex] = option } else { selectedSetItem.options.push(option) } break } case ActionTypes.DELETE_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedItemOptionIndex = selectedItem.options.findIndex(selectedItemOption => selectedItemOption.optionItemId === option.optionItemId) if (selectedItemOptionIndex >= 0) { storeHistory(draft, noHistory) // 刪除 selectedItem.options.splice(selectedItemOptionIndex, 1) } break } case ActionTypes.DELETE_SET_ITEM_OPTION: { const { option, noHistory } = action.payload as {option: IAppBatchItemOption, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems.find(setItem => setItem.key === draft.selectedSetItemKey) if (selectedSetItem == null) return const selectedSetItemOptionIndex = selectedSetItem.options.findIndex(selectedSetItemOption => selectedSetItemOption.optionItemId === option.optionItemId) if (selectedSetItemOptionIndex >= 0) { storeHistory(draft, noHistory) // 刪除 selectedSetItem.options.splice(selectedSetItemOptionIndex, 1) } break } case ActionTypes.TOGGLE_SET_ITEM_TAG: { const { tag, noHistory } = action.payload as {tag: IMenuTag, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) if (selectedItem == null) return const selectedSetItem = selectedItem.setItems.find(setItem => setItem.key === draft.selectedSetItemKey) if (selectedSetItem == null) return storeHistory(draft, noHistory) // 修改 tags const setItemTagIndex = selectedSetItem.tags.findIndex(itemTag => itemTag.name === tag.name) if (setItemTagIndex > -1) { selectedSetItem.tags.splice(setItemTagIndex, 1) } else { selectedSetItem.tags.push(tag) } break } case ActionTypes.UPDATE_MODIFIER: { const { modifier, noHistory } = action.payload as {modifier: IPriceModifier, noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) const selectedSetItem = _.find(selectedItem?.setItems, setItem => setItem.key === draft.selectedSetItemKey) const target = selectedSetItem != null ? selectedSetItem : selectedItem if (target == null) return storeHistory(draft, noHistory) if (target.modifiers == null) { target.modifiers = [modifier] } else { target.modifiers[0] = modifier } break } case ActionTypes.REMOVE_MODIFIER: { const { noHistory } = action.payload as {noHistory: boolean} const selectedItem = draft.selectedBatch.items.find(item => item.key === draft.selectedItemKey) const selectedSetItem = _.find(selectedItem?.setItems, setItem => setItem.key === draft.selectedSetItemKey) const target = selectedSetItem != null ? selectedSetItem : selectedItem if (target == null) return storeHistory(draft, noHistory) target.modifiers = [] break } case ActionTypes.RESET_BATCH: { draft.selectedBatch.items = [] draft.menuItemQuantities = {} draft.batchHistory = initBatchHistory break } case ActionTypes.STASH_BATCH: { // reset batch history draft.batchHistory = initBatchHistory draft.menuItemQuantities = {} // 儲存現在的 batchHistory 到 stashes const history = createBatchHistory(draft) draft.batchStashes.push(history) break } case ActionTypes.DROP_STASH: { const { batchStash } = action.payload as {batchStash: IBatchHistory} _.remove(draft.batchStashes, draftBatchStash => draftBatchStash.createdAt === batchStash.createdAt) break } case ActionTypes.APPLY_STASH: { const { batchStash } = action.payload as {batchStash: IBatchHistory} // reset batch history draft.batchHistory = initBatchHistory draft.selectedBatch = batchStash.batch draft.menuItemQuantities = batchStash.menuItemQuantities break } case ActionTypes.POP_STASH: { const { batchStash } = action.payload as {batchStash: IBatchHistory} const { batch, menuItemQuantities, createdAt } = batchStash // reset batch history draft.batchHistory = initBatchHistory _.remove(draft.batchStashes, draftBatchStash => draftBatchStash.createdAt === createdAt) draft.selectedBatch = batch draft.menuItemQuantities = menuItemQuantities break } case ActionTypes.DROP_ALL_STASH: { draft.batchStashes = [] break } default: { return draft } } }, initialState, )