Implementação de cache para melhoria de performance na navegação Manage time

This commit is contained in:
Paulo Porto
2026-05-27 10:17:36 -03:00
parent 946fab12ca
commit 29a461ea63
3 changed files with 201 additions and 7 deletions

View File

@@ -169,6 +169,7 @@
promise: null, promise: null,
}, },
timeManagementActivityCache: new Map(), timeManagementActivityCache: new Map(),
timeManagementWeekCache: new Map(),
pendingTimeManagementHighlightRowId: "", pendingTimeManagementHighlightRowId: "",
timeManagementDatePicker: { timeManagementDatePicker: {
isOpen: false, isOpen: false,

View File

@@ -125,6 +125,35 @@
throw new Error("resourceCurrentUser.resourcePartyId nao esta disponivel."); 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( const weekInfoPayload = await comcipRepository.fetchInfoWeekRequest(
normalizedDate normalizedDate
); );
@@ -152,11 +181,15 @@
normalizeTimeManagementServiceRequests(srPayload), normalizeTimeManagementServiceRequests(srPayload),
timeEntryRows timeEntryRows
); );
const activitySrNumbers = getActivityRequestSrNumbers( const initialActivitySrNumbers = getInitialActivityRequestSrNumbers(
timeEntryRows,
serviceRequests
);
const fullActivitySrNumbers = getActivityRequestSrNumbers(
srPayload, srPayload,
serviceRequests serviceRequests
); );
const activityRequest = await fetchCachedActivityRequest(activitySrNumbers); const activityRequest = await fetchCachedActivityRequest(initialActivitySrNumbers);
const activityRequestUrl = activityRequest.url; const activityRequestUrl = activityRequest.url;
const activityPayload = activityRequest.payload; const activityPayload = activityRequest.payload;
const activityOptionsBySr = normalizeActivityRequest( const activityOptionsBySr = normalizeActivityRequest(
@@ -205,7 +238,18 @@
isSaving: false, isSaving: false,
saveErrorMessage: "", saveErrorMessage: "",
}; };
writeCachedTimeManagementWeekState(
cacheKey,
appState.timeManagementModal
);
renderModal(); renderModal();
hydrateTimeManagementActivityData({
cacheKey,
requestId,
selectedDate: normalizedDate,
serviceRequests,
srNumbers: fullActivitySrNumbers,
});
} catch (error) { } catch (error) {
if (appState.timeManagementModal?.requestId !== requestId) { if (appState.timeManagementModal?.requestId !== requestId) {
return; return;
@@ -435,6 +479,7 @@
saveErrorMessage: "", saveErrorMessage: "",
}; };
renderModal(); renderModal();
hydrateMissingActivityDataForSr(selectedSrNumber);
} }
function updateTimeManagementField(fieldName, inputValue, rowId = "") { function updateTimeManagementField(fieldName, inputValue, rowId = "") {
@@ -679,6 +724,7 @@
); );
} }
appState.timeManagementWeekCache?.clear?.();
await loadTimeManagementData( await loadTimeManagementData(
modal.selectedDate || getDateInputValue(new Date()) modal.selectedDate || getDateInputValue(new Date())
); );
@@ -821,6 +867,154 @@
?.removeAttribute("aria-activedescendant"); ?.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) { async function fetchCachedActivityRequest(srNumbers) {
const cacheKey = getActivityRequestCacheKey(srNumbers); const cacheKey = getActivityRequestCacheKey(srNumbers);

View File

@@ -254,7 +254,9 @@
function getActivityRequestSrNumbers(srPayload, serviceRequests) { function getActivityRequestSrNumbers(srPayload, serviceRequests) {
const normalizedSrNumbers = [ const normalizedSrNumbers = [
...(serviceRequests || []).map((item) => item.srNumber), ...(serviceRequests || []).map((item) => item.srNumber),
...collectSrNumbers(srPayload), ...extractArray(srPayload, ["items", "data", "results", "content"]).map(
(item) => getFieldValue(item, ["SrNumber", "srNumber"])
),
]; ];
const seen = new Set(); const seen = new Set();
@@ -384,11 +386,8 @@
const preferredRows = ACTIVITY_REQUEST_DETAIL_FIELDS.map((fieldName) => const preferredRows = ACTIVITY_REQUEST_DETAIL_FIELDS.map((fieldName) =>
createActivityRequestDetailRow(fieldName, getActivityRequestDetailValue(item, fieldName)) createActivityRequestDetailRow(fieldName, getActivityRequestDetailValue(item, fieldName))
).filter(Boolean); ).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); const key = normalizeActivityRequestDetailKey(row.key);
if (!key || seen.has(key)) { if (!key || seen.has(key)) {