import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { Id, Product } from '@/lib/product'; import { dimensions_t, ProductData } from "@/lib/dimensions_t"; import uuid from "react-native-uuid"; import { RootState } from '@/app/store'; import { Length } from 'convert'; const initialState = { products: [] as ProductData[], units: "ft", }; export type UpdateAttribute = { product_id: Id, attributeKey: string, attributeValue: any, } export type UpdateAttributeKey = { product_id: Id, oldKey: string, newKey: string, } export type AddAttribute = { product_id: Id, } const cp = (obj: any) => JSON.parse(JSON.stringify(obj)); const productsState = createSlice({ name: 'products-slice', initialState, reducers: { setUnits(state, action : PayloadAction) { state.units = action.payload; }, createProduct(state, action: PayloadAction) { if (!state) { return initialState } const product = action.payload; if (!product.id) product.id = uuid.v4().toString(); state.products = [...state.products, action.payload]; return state; }, deleteProduct(state, action: PayloadAction) { if (!state) return initialState; return { ...state, products: [...state.products.filter((prod) => { return prod.id?.valueOf() !== action.payload.valueOf(); })], } }, updateAttribute(state, action: PayloadAction) { const { product_id, attributeKey, attributeValue } = action.payload if (!state) return initialState; return { ...state, products: state.products.map(prod => { if (prod.id !== product_id) return prod; const attributes = cp(prod.attributes); attributes[attributeKey] = attributeValue; return { ...prod, attributes, } }) }; }, changeKey(state, action: PayloadAction) { if (!state) return initialState; const { product_id, oldKey, newKey } = action.payload return { ...state, products: state.products.map(prod => { if (prod.id !== product_id) return prod; const attributes = cp(prod.attributes); attributes[newKey] = attributes[oldKey]; delete attributes[oldKey]; attributes.id = prod.id; return { ...prod, attributes, } }) }; }, addAttribute(state, action: PayloadAction) { if (!state) return initialState; const product_id = action.payload; state.products = state.products.map(prod => { if (prod.id !== product_id) return prod; const i = (Object.keys(prod.attributes || {}).filter(k => k.match(/attribute [\d]+/)) || []).length; const newAttribute = `attribute ${i + 1}`; return { ...prod, attributes: { ...prod.attributes, [newAttribute]: `value`, } } }); return state; }, deleteAttribute(state, action: PayloadAction<{ product_id: Id, attribute: string }>) { if (!state) return initialState; const { product_id, attribute } = action.payload; return { ...state, products: state.products.map(prod => { if (prod.id !== product_id) return prod; const attributes = Object.fromEntries(Object.entries(prod).filter(([k, v]) => (k !== attribute))); return { ...prod, attributes, } }), }; }, updatePrice(state, action: PayloadAction<{ product_id: Id, pricePerUnit: number }>) { if (!state) return initialState; const { product_id, pricePerUnit } = action.payload; state.products = state.products.map(prod => { if (prod.id !== product_id) return prod; prod.pricePerUnit = pricePerUnit; return prod; }); return state; }, updateDimensions(state, action: PayloadAction<{ product_id: Id, dimensions: dimensions_t }>) { if (!state) return initialState; const { product_id, dimensions } = action.payload; console.log("Changing dimensions: %o", action.payload); return { ...state, products: state.products.map(prod => { if (prod.id !== product_id) return prod; return { ...prod, dimensions, } }), }; }, } }); export const selectProductsDatas = (state: RootState) => { return state.products; } export const selectUnits = (state : RootState) => { return (state.units || "ft") as Length; } export const selectProducts = createSelector([selectProductsDatas], productsData => { return productsData.map(d => Product.fromObject(d)); }) export const selectProductIds = createSelector([selectProducts], products => { return products.map(p => p.id); }) export const selectProductAttributes = createSelector([selectProducts], products => { return Object.fromEntries(products.map(p => { return [ p.id, p.attributesAsList, ] })) }) export const actions = { ...productsState.actions }; export const { setUnits, createProduct, deleteProduct, changeKey, updateAttribute, addAttribute, deleteAttribute, updatePrice, updateDimensions, } = productsState.actions; export default productsState.reducer;