diff --git a/content-script.js b/content-script.js index 5d32520..cfbda10 100644 --- a/content-script.js +++ b/content-script.js @@ -169,6 +169,7 @@ promise: null, }, timeManagementActivityCache: new Map(), + timeManagementWeekCache: new Map(), pendingTimeManagementHighlightRowId: "", timeManagementDatePicker: { isOpen: false, diff --git a/src/content/manage-time-controller.js b/src/content/manage-time-controller.js index d860220..e6f12ad 100644 --- a/src/content/manage-time-controller.js +++ b/src/content/manage-time-controller.js @@ -125,6 +125,35 @@ throw new Error("resourceCurrentUser.resourcePartyId nao esta disponivel."); } + const cacheKey = getTimeManagementWeekCacheKey( + resourcePartyId, + normalizedDate + ); + const cachedState = getCachedTimeManagementWeekState(cacheKey); + + if (cachedState) { + appState.timeManagementModal = { + ...cachedState, + requestId, + selectedDate: normalizedDate, + status: "ready", + isSaving: false, + saveErrorMessage: "", + }; + renderModal(); + hydrateTimeManagementActivityData({ + cacheKey, + requestId, + selectedDate: normalizedDate, + serviceRequests: cachedState.serviceRequests || [], + srNumbers: getActivityRequestSrNumbers( + null, + cachedState.serviceRequests || [] + ), + }); + return; + } + const weekInfoPayload = await comcipRepository.fetchInfoWeekRequest( normalizedDate ); @@ -152,11 +181,15 @@ normalizeTimeManagementServiceRequests(srPayload), timeEntryRows ); - const activitySrNumbers = getActivityRequestSrNumbers( + const initialActivitySrNumbers = getInitialActivityRequestSrNumbers( + timeEntryRows, + serviceRequests + ); + const fullActivitySrNumbers = getActivityRequestSrNumbers( srPayload, serviceRequests ); - const activityRequest = await fetchCachedActivityRequest(activitySrNumbers); + const activityRequest = await fetchCachedActivityRequest(initialActivitySrNumbers); const activityRequestUrl = activityRequest.url; const activityPayload = activityRequest.payload; const activityOptionsBySr = normalizeActivityRequest( @@ -205,7 +238,18 @@ isSaving: false, saveErrorMessage: "", }; + writeCachedTimeManagementWeekState( + cacheKey, + appState.timeManagementModal + ); renderModal(); + hydrateTimeManagementActivityData({ + cacheKey, + requestId, + selectedDate: normalizedDate, + serviceRequests, + srNumbers: fullActivitySrNumbers, + }); } catch (error) { if (appState.timeManagementModal?.requestId !== requestId) { return; @@ -435,6 +479,7 @@ saveErrorMessage: "", }; renderModal(); + hydrateMissingActivityDataForSr(selectedSrNumber); } function updateTimeManagementField(fieldName, inputValue, rowId = "") { @@ -679,6 +724,7 @@ ); } + appState.timeManagementWeekCache?.clear?.(); await loadTimeManagementData( modal.selectedDate || getDateInputValue(new Date()) ); @@ -821,6 +867,154 @@ ?.removeAttribute("aria-activedescendant"); } + function getInitialActivityRequestSrNumbers(timeEntryRows, serviceRequests) { + const rowSrNumbers = (timeEntryRows || []) + .map((row) => cleanString(row?.selectedSrNumber)) + .filter((srNumber) => /^SR\d+$/i.test(srNumber)); + + return rowSrNumbers.length + ? dedupeSrNumbers(rowSrNumbers) + : dedupeSrNumbers((serviceRequests || []).slice(0, 1).map((item) => item.srNumber)); + } + + function dedupeSrNumbers(srNumbers) { + const seen = new Set(); + + return (srNumbers || []) + .map((srNumber) => cleanString(srNumber).toUpperCase()) + .filter((srNumber) => /^SR\d+$/i.test(srNumber)) + .filter((srNumber) => { + const key = normalizeString(srNumber); + + if (seen.has(key)) { + return false; + } + + seen.add(key); + return true; + }); + } + + function getTimeManagementWeekCacheKey(resourceId, selectedDate) { + return `${cleanString(resourceId)}|${cleanString(selectedDate)}`; + } + + function getCachedTimeManagementWeekState(cacheKey) { + const cachedState = appState.timeManagementWeekCache?.get?.(cacheKey); + + return cachedState ? cloneTimeManagementState(cachedState) : null; + } + + function writeCachedTimeManagementWeekState(cacheKey, state) { + if (!cacheKey || !appState.timeManagementWeekCache?.set) { + return; + } + + appState.timeManagementWeekCache.set( + cacheKey, + cloneTimeManagementState({ + ...state, + requestId: "", + isSaving: false, + saveErrorMessage: "", + highlightRowId: "", + }) + ); + } + + function cloneTimeManagementState(state) { + if (!state) { + return null; + } + + if (typeof structuredClone === "function") { + return structuredClone(state); + } + + return JSON.parse(JSON.stringify(state)); + } + + async function hydrateTimeManagementActivityData({ + cacheKey, + requestId, + selectedDate, + serviceRequests, + srNumbers, + }) { + const fullCacheKey = getActivityRequestCacheKey(srNumbers); + + if (!fullCacheKey) { + return; + } + + try { + const activityRequest = await fetchCachedActivityRequest(srNumbers); + const activityPayload = activityRequest.payload; + const activityOptionsBySr = normalizeActivityRequest( + activityPayload, + serviceRequests + ); + const activityRequestDetailsBySr = normalizeActivityRequestDetails( + activityPayload, + serviceRequests + ); + + if ( + appState.timeManagementModal?.requestId !== requestId || + cleanString(appState.timeManagementModal?.selectedDate) !== cleanString(selectedDate) + ) { + return; + } + + appState.timeManagementModal = { + ...appState.timeManagementModal, + activityOptionsBySr: { + ...(appState.timeManagementModal.activityOptionsBySr || {}), + ...activityOptionsBySr, + }, + activityRequestDetailsBySr: { + ...(appState.timeManagementModal.activityRequestDetailsBySr || {}), + ...activityRequestDetailsBySr, + }, + activityRequestUrl: activityRequest.url || appState.timeManagementModal.activityRequestUrl, + activityRequestPayload: activityPayload || appState.timeManagementModal.activityRequestPayload, + }; + writeCachedTimeManagementWeekState(cacheKey, appState.timeManagementModal); + renderModal(); + } catch { + // Background activity enrichment should not block Manage time usage. + } + } + + function hydrateMissingActivityDataForSr(srNumber) { + const modal = appState.timeManagementModal; + const normalizedSrNumber = cleanString(srNumber).toUpperCase(); + + if ( + !modal || + !/^SR\d+$/i.test(normalizedSrNumber) || + getTimeManagementActivityOptions(modal, normalizedSrNumber).some( + (option) => cleanString(option?.value) + ) + ) { + return; + } + + const resourceId = getCurrentUserResourcePartyId(); + const cacheKey = getTimeManagementWeekCacheKey( + resourceId, + modal.selectedDate + ); + + hydrateTimeManagementActivityData({ + cacheKey, + requestId: modal.requestId, + selectedDate: modal.selectedDate, + serviceRequests: modal.serviceRequests || [], + srNumbers: [normalizedSrNumber], + }); + } + async function fetchCachedActivityRequest(srNumbers) { const cacheKey = getActivityRequestCacheKey(srNumbers); diff --git a/src/domain/time-management-data.js b/src/domain/time-management-data.js index 45d746d..f464040 100644 --- a/src/domain/time-management-data.js +++ b/src/domain/time-management-data.js @@ -254,7 +254,9 @@ function getActivityRequestSrNumbers(srPayload, serviceRequests) { const normalizedSrNumbers = [ ...(serviceRequests || []).map((item) => item.srNumber), - ...collectSrNumbers(srPayload), + ...extractArray(srPayload, ["items", "data", "results", "content"]).map( + (item) => getFieldValue(item, ["SrNumber", "srNumber"]) + ), ]; const seen = new Set(); @@ -384,11 +386,8 @@ const preferredRows = ACTIVITY_REQUEST_DETAIL_FIELDS.map((fieldName) => createActivityRequestDetailRow(fieldName, getActivityRequestDetailValue(item, fieldName)) ).filter(Boolean); - const extraRows = flattenDisplayRecord(item) - .map(([fieldName, value]) => createActivityRequestDetailRow(fieldName, value)) - .filter(Boolean); - return [...preferredRows, ...extraRows].filter((row) => { + return preferredRows.filter((row) => { const key = normalizeActivityRequestDetailKey(row.key); if (!key || seen.has(key)) {