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,
},
timeManagementActivityCache: new Map(),
timeManagementWeekCache: new Map(),
pendingTimeManagementHighlightRowId: "",
timeManagementDatePicker: {
isOpen: false,

View File

@@ -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);

View File

@@ -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)) {