[OBPIH-6627] keep progress when reload on confirm page of full outbound import#4802
Conversation
- move outbound import steps into a separate file - include label and name on requested by validation schema
| const setValue = (newValue) => sessionStorage.setItem(key, JSON.stringify(newValue)); | ||
| const clearValue = () => sessionStorage.removeItem(key); | ||
|
|
||
| return [val, setValue, clearValue]; |
There was a problem hiding this comment.
wouldn't it be better to return it as object, so we would be able to get rid of the confusing:
const [cachedData,]it would get even more weird if we wanted to use the first and the third property:
const [cachedData, null, clearValue]There was a problem hiding this comment.
I modeled this useSessionStorage hook after useState where we destruct the value and setter from an array and accessing nth value like [,,clearFunction] = useSessionStorage() is rather common and is aligned with react conventions.
I don't feel like one solution is better than the other, it's what we prefer, both are correct.
There was a problem hiding this comment.
ok, for me it just looks weird when we have a comma without anything after that, but if you feel like it's better, then I'm fine, it was not a change request
| if (_.isEmpty(cachedData) && !is(OutboundImportStep.DETAILS)) { | ||
| navigateToStep(OutboundImportStep.DETAILS); | ||
| } | ||
| }, []); |
There was a problem hiding this comment.
let's move it to a separate small hook named e.g. useCachedData
There was a problem hiding this comment.
shouldn't the cachedData (or any property of that, since this is probably an object that needs to be compared properly) be the dependency of the useEffect? Is there a possibility that those would be loaded/recognized after the useEffect is run?
There was a problem hiding this comment.
values from localStorage and sessionStorage are not related to state and will not trigger the component rerender even if you put the in the dependency array.
Also these values are synchronous unlike useEffect which is asynchronous which means that these values will be available in the hook without a delay.
| event.preventDefault(); | ||
| submitMethod(getValues()); | ||
| }; | ||
|
|
There was a problem hiding this comment.
I kinda do not get this method. Why do we want to skip validation? And which validation exactly? Is it the frontend validation of the file from previous step? Even if so, how do you distinguish here whether the submit happens from the refreshed page or from regular usage of the workflow?
There was a problem hiding this comment.
The problem is that I don't cache the uploaded file packingList which is a required value in the validation schema,
So after loading all of the values from sessionStorage I am missing the packingList File which prevents me from submitting the form.
I figured that there are no more fields that we need to fill out on the CONFIRM step and all of the validation should have happened in the previous step so we can assume that the data that we have on confirm step is correct and as a workaround we can skip the validation.
There was a problem hiding this comment.
Ok, I supposed this was the case, thanks for explaining
| CONFIRM: 'CONFIRM', | ||
| }; | ||
|
|
||
| export default OutboundImportStep; |
There was a problem hiding this comment.
let's move it to consts directory, where we place all the enums.
| * and that cached data is not relevant anymore | ||
| * */ | ||
| if (!_.isEmpty(cachedData)) { | ||
| if (OutboundImportStep.CONFIRM === queryParams?.step) { |
There was a problem hiding this comment.
I will probably get to this below, but why do we suddenly need to store the step in the query params, when we should have access to the current step from the useWizard hook?
There was a problem hiding this comment.
since we are dealing with page refreshes I needed know what page are we reloading, if we were to use just state for storing current step it would be cleared after the refresh and we wouldn't know if we need to load the cached data (when we are on CONFIRM step) or not load it when we are for example on DETAILS step
There was a problem hiding this comment.
thanks for explaining, I forgot to delete this comment after I figured out below what the case is.
| } | ||
|
|
||
| return values; | ||
| }, [currentLocation]); |
There was a problem hiding this comment.
shouldn't this include the cachedData and queryParams? (don't know if those are objects or not, so make sure this is compared properly).
Moreover - the dependency should be currentLocation?.id, to properly compare objects.
| setTableData([...tableDataGrouped.itemsWithErrors, ...tableDataGrouped.itemsWithoutErrors]); | ||
| } | ||
| }; | ||
|
|
There was a problem hiding this comment.
thanks for moving that out, I was about to do such cleanups when I am back. I know I might've left a lot of 🍝 with a potential to cleanup, but I focused on the functionality at that time.
| const loadCachedData = async () => { | ||
| if (!_.isEmpty(cachedData)) { | ||
| spinner.show(); | ||
| setPackingListData(cachedData.packingList); |
There was a problem hiding this comment.
one of the comments above - why isn't packing list set there, where the rest of the values?
There was a problem hiding this comment.
either set all the values here (probably more appropriate, but I am not sure if the react-hook-form would handle that well via setValue) or in the defaultValues, unless I am not aware of some edge cases why you separated those two.
There was a problem hiding this comment.
There is one edge case - the packingList that is displayed in the table differs from the one that we want to send to the API. I can see that you store only the one, that is sent to the API, you do not store the tableData.
The difference is that the table data contains fetched e.g. product, so we display the product name in the table, whereas the packingList that is sent to the API, contains only product id and if we were about to use it in the table, we would not display the product name.
| }; | ||
| }, { itemsWithErrors: [], itemsWithoutErrors: [] }); | ||
|
|
||
| const validatePackingListItems = async (fulfilmentPayload) => { |
There was a problem hiding this comment.
the name of the method is misleading though - we validate the whole outbound at this point, not only the items.
| */ | ||
| const currentStepKey = useMemo(() => | ||
| parsedQueryParams.step || initialKey || steps[0]?.key, | ||
| [parsedQueryParams.step, steps, initialKey]); |
There was a problem hiding this comment.
are we sure the changes of the query params are recognized "live" by React?
✨ Description of Change
Link to GitHub issue or Jira ticket: OBPIH-6627
Description:
A requested feature for the Full Outbound Import was to implement an ability for a user to refresh the page and not lose the filled-out form as we have in other workflows.
One big difference between full outbound import and other create stock movement workflows is that in the latter we save the data in the database and upon refreshing the page we full this created Pendning stock movement,but as for full outbound import, we are only saving it at the end so when we refresh the page we lose all the data.
To somehow save this filled-out data I decide to store it in the
sessionStoragewhich is one of the browser storages that we can access.I decided to use
sessionStorageandlocalStoragebecause of the one main difference, sessionStorage expires once the tab is closed, unlike localStorage which has to be cleared manually. Since we are working with temporary data that we don't care to save for too longsessionStorageseemed like the approach to take.To manage sessionStorage I implemented a custom
useSessionStoragehook. kind of mimicking theuseStateAnother big change was a slight modification to the
useWizzardhook.Since we are dealing with page refreshes, we need to know what step we are refreshing, If user opens
CONFIRMstep and there is cached data we want to render that step with prefilled cached data, and if user opensDETAILSpage and there is available cached data we want to assume that user wants to do a new import and we should not load cached data.So I modified the
useWizzardhook so we switch between steps based on the URL query parameterstepinstead of a value held instate📷 Screenshots & Recordings (optional)
- No screenshots -
📈 Test Plan
Description of test plan (if applicable):
✅ Quality Checks
[OBS-123]for Jira,[#0000]for GitHub, or[OBS-123, OBPIH-123]if there are multiple), or with[N/A]if not applicable