import { createReducerObject } from 'create-reducer-object'; import { DOC_CREATED, DOC_CREATE_RESPONDED, DOC_READ, DOC_READ_RESPONDED, DOC_UPDATED, DOC_UPDATE_RESPONDED, DOC_DELETED, DOC_DELETE_RESPONDED } from 'constants/actions'; export const initialState = {}; function insertDoc(routeDocs, id, pending, fields) { return [ ...routeDocs, { id, pending, ...fields } ]; } function updateDoc(routeDocs, index, id, pending, fields = {}) { if (index === -1) { return insertDoc(routeDocs, id, false, fields); } return [ ...routeDocs.slice(0, index - 1), { ...routeDocs[index], id, pending, ...fields }, ...routeDocs.slice(index + 1) ]; } function deleteDoc(docs, index) { return docs.slice(0, index - 1) .concat(docs.slice(index + 1)); } function getRouteDocs(state, route) { if (state[route]) { return state[route].items; } return []; } function onCreate(state, { route, pendingId, fields }) { if (state[route] && state[route].loading) { return {}; } const routeDocs = getRouteDocs(state, route); return { [route]: { loading: true, items: insertDoc(routeDocs, pendingId, true, fields) } }; } function onCreateResponse(state, { route, pendingId, err, response }) { const routeDocs = getRouteDocs(state, route); const index = routeDocs.findIndex(({ id }) => id === pendingId); if (err) { const newRoute = { ...(state[route] || {}), error: true }; if (index === -1) { return { [route]: newRoute }; } return { [route]: { ...newRoute, items: deleteDoc(routeDocs, index) } }; } const { id: actualId, ...fields } = response.data; return { [route]: { ...state[route], items: updateDoc(routeDocs, index, actualId, false, fields) } }; } function onRead(state, { route }) { if (state[route]) { return { [route]: { ...state[route], loading: true } }; } return { [route]: { loading: true, items: [] } }; } function onReadResponse(state, { route, err, response }) { if (err) { return { [route]: { ...(state[route] || {}), loading: false, error: true } }; } return { [route]: { ...(state[route] || {}), loading: false, error: false, items: response.data[route] } }; } function onUpdate(state, { route, id, fields }) { if (state[route] && state[route].loading) { return {}; } const routeDocs = getRouteDocs(state, route); const index = routeDocs.findIndex(({ id: docId }) => docId === id); return { [route]: { loading: true, items: updateDoc(routeDocs, index, id, true, fields) } }; } function onUpdateResponse(state, { route, id, err }) { const routeDocs = getRouteDocs(state, route); const index = routeDocs.findIndex(({ id: docId }) => docId === id); const newItems = updateDoc(routeDocs, index, id, false); if (err) { const newRoute = { ...(state[route] || {}), error: true }; if (index === -1) { return { [route]: newRoute }; } return { [route]: { ...newRoute, items: newItems } }; } return { [route]: { ...state[route], error: false, items: newItems } }; } function onDelete(state, { route, id }) { const routeDocs = getRouteDocs(state, route); const index = routeDocs.findIndex(({ id: docId }) => docId === id); return { [route]: { ...state[route], error: false, items: updateDoc(routeDocs, index, id, true) } }; } function onDeleteResponse(state, { route, id, err }) { const routeDocs = getRouteDocs(state, route); const index = routeDocs.findIndex(({ id: docId }) => docId === id); if (err) { return { [route]: { ...state[route], error: true, items: updateDoc(routeDocs, index, id, false) } }; } return { [route]: { ...state[route], error: false, items: deleteDoc(routeDocs, index) } }; } const reducerMap = { [DOC_CREATED]: onCreate, [DOC_CREATE_RESPONDED]: onCreateResponse, [DOC_READ]: onRead, [DOC_READ_RESPONDED]: onReadResponse, [DOC_UPDATED]: onUpdate, [DOC_UPDATE_RESPONDED]: onUpdateResponse, [DOC_DELETED]: onDelete, [DOC_DELETE_RESPONDED]: onDeleteResponse }; export const crud = createReducerObject(reducerMap, initialState);