Add article pack units with Redux-cached company unit list.
Edit amount/unit on the price tile (pc×1 = no pack unit), manage units in a dialog, and keep the default pc unit non-deletable.
This commit is contained in:
98
src/store/unitSlice.js
Normal file
98
src/store/unitSlice.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import { unit as unitApi } from '../api';
|
||||
import { requireArray, requireObject } from '../utils/apiShape';
|
||||
import {
|
||||
bootstrapSession,
|
||||
createCompany,
|
||||
loginWithGoogle,
|
||||
logout,
|
||||
selectCompany,
|
||||
} from './authSlice';
|
||||
|
||||
function sortUnits(units) {
|
||||
return [...units].sort((a, b) => {
|
||||
const left = a.symbol ?? a.si ?? '';
|
||||
const right = b.symbol ?? b.si ?? '';
|
||||
return String(left).localeCompare(String(right), 'de');
|
||||
});
|
||||
}
|
||||
|
||||
export const loadUnits = createAsyncThunk('unit/load', async () => {
|
||||
const res = await unitApi.list();
|
||||
requireObject(res, 'GET /api/unit');
|
||||
return requireArray(res.units, 'units');
|
||||
});
|
||||
|
||||
export const upsertUnit = createAsyncThunk('unit/upsert', async (body) => {
|
||||
const res = await unitApi.upsert(body);
|
||||
requireObject(res, 'PUT /api/unit');
|
||||
requireObject(res.unit, 'unit');
|
||||
return res.unit;
|
||||
});
|
||||
|
||||
export const deleteUnit = createAsyncThunk('unit/delete', async (si) => {
|
||||
await unitApi.delete(si);
|
||||
return si;
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
units: [],
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
const unitSlice = createSlice({
|
||||
name: 'unit',
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(loadUnits.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
state.error = null;
|
||||
})
|
||||
.addCase(loadUnits.fulfilled, (state, action) => {
|
||||
state.status = 'succeeded';
|
||||
state.units = sortUnits(action.payload);
|
||||
})
|
||||
.addCase(loadUnits.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.error.message;
|
||||
})
|
||||
.addCase(upsertUnit.fulfilled, (state, action) => {
|
||||
const row = action.payload;
|
||||
state.units = sortUnits([
|
||||
...state.units.filter((u) => u.si !== row.si),
|
||||
row,
|
||||
]);
|
||||
})
|
||||
.addCase(deleteUnit.fulfilled, (state, action) => {
|
||||
state.units = state.units.filter((u) => u.si !== action.payload);
|
||||
})
|
||||
.addCase(selectCompany.fulfilled, () => initialState)
|
||||
.addCase(createCompany.fulfilled, () => initialState)
|
||||
.addCase(logout.fulfilled, () => initialState);
|
||||
},
|
||||
});
|
||||
|
||||
export const selectUnits = (state) => state.unit.units;
|
||||
export const selectUnitsStatus = (state) => state.unit.status;
|
||||
export const selectUnitsError = (state) => state.unit.error;
|
||||
|
||||
export function unitLoadListener(listenerMiddleware) {
|
||||
listenerMiddleware.startListening({
|
||||
matcher: (action) =>
|
||||
bootstrapSession.fulfilled.match(action) ||
|
||||
loginWithGoogle.fulfilled.match(action) ||
|
||||
selectCompany.fulfilled.match(action) ||
|
||||
createCompany.fulfilled.match(action),
|
||||
effect: async (_action, api) => {
|
||||
const companyId = api.getState().auth.user?.selectedCompanyId;
|
||||
if (companyId) {
|
||||
api.dispatch(loadUnits());
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default unitSlice.reducer;
|
||||
Reference in New Issue
Block a user