945 lines
28 KiB
JavaScript
945 lines
28 KiB
JavaScript
(() => {
|
|
function create(dependencies) {
|
|
const {
|
|
appState,
|
|
COMCIP_REQUEST,
|
|
comcipRepository,
|
|
readCachedTaskTypePayload,
|
|
writeCachedTaskTypePayload,
|
|
getStoredTimeManagementSelectedDate,
|
|
setStoredTimeManagementSelectedDate,
|
|
renderModal,
|
|
cleanString,
|
|
normalizeString,
|
|
getDateInputValue,
|
|
parseDatePreservingDateOnly,
|
|
getFieldValue,
|
|
extractArray,
|
|
getErrorMessage,
|
|
cssEscape,
|
|
getTimeManagementRows,
|
|
isTimeManagementSaveEnabled,
|
|
getTimeManagementPendingChanges,
|
|
createTimeManagementSavePayload,
|
|
isNonServiceRequestSrNumber,
|
|
normalizeInfoWeekResponse,
|
|
normalizeTimeManagementServiceRequests,
|
|
mergeTimeManagementServiceRequests,
|
|
normalizeTimeManagementEntryRows,
|
|
normalizeTimeManagementEntryRowSelection,
|
|
getActivityRequestSrNumbers,
|
|
normalizeActivityRequest,
|
|
normalizeTaskTypeRequest,
|
|
getTimeManagementTaskTypeOptions,
|
|
getTimeManagementActivityOptions,
|
|
findTimeManagementServiceRequest,
|
|
findOptionValue,
|
|
} = dependencies;
|
|
|
|
let requestCounter = 0;
|
|
|
|
function toggleTimeManagementDatePicker() {
|
|
const selectedDate = getTimeManagementSelectedDate();
|
|
const pickerDate = parseDatePreservingDateOnly(selectedDate);
|
|
const fallback = Number.isNaN(pickerDate.getTime()) ? new Date() : pickerDate;
|
|
|
|
appState.timeManagementDatePicker = {
|
|
isOpen: !appState.timeManagementDatePicker.isOpen,
|
|
year: fallback.getFullYear(),
|
|
month: fallback.getMonth(),
|
|
};
|
|
renderModal();
|
|
}
|
|
|
|
function shiftTimeManagementDatePickerMonth(delta) {
|
|
const picker = appState.timeManagementDatePicker;
|
|
const nextDate = new Date(picker.year, picker.month + delta, 1);
|
|
|
|
appState.timeManagementDatePicker = {
|
|
isOpen: true,
|
|
year: nextDate.getFullYear(),
|
|
month: nextDate.getMonth(),
|
|
};
|
|
renderModal();
|
|
}
|
|
|
|
async function selectTimeManagementDate(selectedDate) {
|
|
appState.timeManagementDatePicker = {
|
|
...appState.timeManagementDatePicker,
|
|
isOpen: false,
|
|
};
|
|
await loadTimeManagementData(selectedDate);
|
|
}
|
|
|
|
async function shiftTimeManagementDateByWeeks(delta) {
|
|
const selectedDate = getTimeManagementSelectedDate();
|
|
const parsedDate = parseDatePreservingDateOnly(selectedDate);
|
|
const baseDate = Number.isNaN(parsedDate.getTime())
|
|
? new Date()
|
|
: parsedDate;
|
|
|
|
baseDate.setDate(baseDate.getDate() + (Number(delta) || 0) * 7);
|
|
appState.timeManagementDatePicker = {
|
|
...appState.timeManagementDatePicker,
|
|
isOpen: false,
|
|
};
|
|
await loadTimeManagementData(getDateInputValue(baseDate));
|
|
}
|
|
|
|
function getTimeManagementSelectedDate() {
|
|
return appState.timeManagementModal?.selectedDate || getDateInputValue(new Date());
|
|
}
|
|
|
|
async function openTimeManagementModal() {
|
|
const selectedDate = getStoredTimeManagementSelectedDate();
|
|
|
|
appState.timeManagementModal = createTimeManagementLoadingState(selectedDate);
|
|
renderModal();
|
|
await loadTimeManagementData(selectedDate);
|
|
}
|
|
|
|
async function loadTimeManagementData(selectedDate) {
|
|
const normalizedDate = cleanString(selectedDate) || getDateInputValue(new Date());
|
|
const requestId = `${Date.now()}-${++requestCounter}`;
|
|
|
|
setStoredTimeManagementSelectedDate(normalizedDate);
|
|
appState.timeManagementDatePicker = {
|
|
...appState.timeManagementDatePicker,
|
|
isOpen: false,
|
|
};
|
|
appState.timeManagementModal = {
|
|
...(appState.timeManagementModal || {}),
|
|
requestId,
|
|
selectedDate: normalizedDate,
|
|
status: "loading",
|
|
errorMessage: "",
|
|
};
|
|
renderModal();
|
|
|
|
try {
|
|
const resourcePartyId = getCurrentUserResourcePartyId();
|
|
const taskTypePromise = fetchTaskTypeRequest();
|
|
|
|
if (!resourcePartyId) {
|
|
throw new Error("resourceCurrentUser.resourcePartyId nao esta disponivel.");
|
|
}
|
|
|
|
const weekInfoPayload = await comcipRepository.fetchInfoWeekRequest(
|
|
normalizedDate
|
|
);
|
|
const weekDays = normalizeInfoWeekResponse(weekInfoPayload);
|
|
const weekId = cleanString(weekDays[0]?.weekId);
|
|
|
|
if (!weekId) {
|
|
throw new Error("WeekId nao encontrado no retorno de infoWeekRequest.");
|
|
}
|
|
|
|
const timeEntryWeekUrl = comcipRepository.buildTimeEntryWeekUrl(
|
|
resourcePartyId,
|
|
weekId
|
|
);
|
|
const [srPayload, taskTypePayload, timeEntryWeekPayload] = await Promise.all([
|
|
comcipRepository.fetchActiveServiceRequests(resourcePartyId, weekId),
|
|
taskTypePromise,
|
|
comcipRepository.fetchTimeEntryWeek(timeEntryWeekUrl),
|
|
]);
|
|
const timeEntryRows = normalizeTimeManagementEntryRows(
|
|
timeEntryWeekPayload,
|
|
weekDays
|
|
);
|
|
const serviceRequests = mergeTimeManagementServiceRequests(
|
|
normalizeTimeManagementServiceRequests(srPayload),
|
|
timeEntryRows
|
|
);
|
|
const activitySrNumbers = getActivityRequestSrNumbers(
|
|
srPayload,
|
|
serviceRequests
|
|
);
|
|
const activityRequest = await fetchCachedActivityRequest(activitySrNumbers);
|
|
const activityRequestUrl = activityRequest.url;
|
|
const activityPayload = activityRequest.payload;
|
|
const activityOptionsBySr = normalizeActivityRequest(
|
|
activityPayload,
|
|
serviceRequests
|
|
);
|
|
const taskTypes = normalizeTaskTypeRequest(taskTypePayload);
|
|
const rows = timeEntryRows;
|
|
const selectedSrNumber = rows[0]?.selectedSrNumber || "";
|
|
const normalizedRows = rows.map((row, index) =>
|
|
normalizeTimeManagementEntryRowSelection(
|
|
row,
|
|
index,
|
|
activityOptionsBySr,
|
|
taskTypes
|
|
)
|
|
);
|
|
|
|
if (appState.timeManagementModal?.requestId !== requestId) {
|
|
return;
|
|
}
|
|
|
|
appState.timeManagementModal = {
|
|
requestId,
|
|
selectedDate: normalizedDate,
|
|
status: "ready",
|
|
errorMessage: "",
|
|
weekId,
|
|
weekDays,
|
|
serviceRequests,
|
|
selectedSrNumber,
|
|
activityOptionsBySr,
|
|
activityRequestUrl,
|
|
activityRequestPayload: activityPayload,
|
|
timeEntryRows: normalizedRows,
|
|
selectedActivityValue: normalizedRows[0]?.selectedActivityValue || "",
|
|
taskTypes,
|
|
selectedTaskTypeValue: normalizedRows[0]?.selectedTaskTypeValue || "",
|
|
highlightRowId: "",
|
|
deletedTimeEntryIds: [],
|
|
isSaving: false,
|
|
saveErrorMessage: "",
|
|
};
|
|
renderModal();
|
|
} catch (error) {
|
|
if (appState.timeManagementModal?.requestId !== requestId) {
|
|
return;
|
|
}
|
|
|
|
appState.timeManagementModal = {
|
|
...(appState.timeManagementModal || {}),
|
|
requestId,
|
|
selectedDate: normalizedDate,
|
|
status: "error",
|
|
errorMessage: getErrorMessage(error),
|
|
};
|
|
renderModal();
|
|
}
|
|
}
|
|
|
|
function createTimeManagementLoadingState(selectedDate) {
|
|
return {
|
|
requestId: "",
|
|
selectedDate,
|
|
status: "loading",
|
|
errorMessage: "",
|
|
weekId: "",
|
|
weekDays: [],
|
|
serviceRequests: [],
|
|
selectedSrNumber: "",
|
|
activityOptionsBySr: {},
|
|
activityRequestUrl: "",
|
|
activityRequestPayload: null,
|
|
selectedActivityValue: "",
|
|
taskTypes: [],
|
|
selectedTaskTypeValue: "",
|
|
timeEntryRows: [],
|
|
highlightRowId: "",
|
|
deletedTimeEntryIds: [],
|
|
isSaving: false,
|
|
saveErrorMessage: "",
|
|
};
|
|
}
|
|
|
|
function createBlankTimeManagementEntryRow(srNumber = "", source = "blank") {
|
|
return {
|
|
id: `row-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
timeEntryId: "",
|
|
selectedSrNumber: cleanString(srNumber),
|
|
selectedActivityValue: "",
|
|
selectedTaskTypeValue: "",
|
|
dayHours: {},
|
|
source,
|
|
originalSignature: "",
|
|
};
|
|
}
|
|
|
|
function addTimeManagementEntryRow(type) {
|
|
if (!appState.timeManagementModal) {
|
|
return;
|
|
}
|
|
|
|
const existingDuplicateRow = findExistingTimeManagementDuplicateRow(
|
|
appState.timeManagementModal,
|
|
type
|
|
);
|
|
|
|
if (existingDuplicateRow) {
|
|
highlightTimeManagementDuplicateRow(existingDuplicateRow.id);
|
|
return;
|
|
}
|
|
|
|
const srNumber = type === "non-service" ? "non-service-sr" : "";
|
|
const nextRow = createBlankTimeManagementEntryRow(srNumber, "manual");
|
|
const nextRows = [
|
|
...getTimeManagementRows(appState.timeManagementModal),
|
|
nextRow,
|
|
];
|
|
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
timeEntryRows: nextRows,
|
|
highlightRowId: "",
|
|
saveErrorMessage: "",
|
|
};
|
|
appState.pendingFocusSelector = getTimeManagementComboboxFocusSelector(
|
|
nextRow.id,
|
|
type === "non-service" ? "taskType" : "sr"
|
|
);
|
|
renderModal();
|
|
}
|
|
|
|
function removeTimeManagementEntryRow(rowId) {
|
|
if (!appState.timeManagementModal) {
|
|
return;
|
|
}
|
|
|
|
const normalizedRowId = cleanString(rowId);
|
|
const rows = getTimeManagementRows(appState.timeManagementModal);
|
|
const removedRow = rows.find(
|
|
(row) => cleanString(row?.id) === normalizedRowId
|
|
);
|
|
const removedTimeEntryId = cleanString(removedRow?.timeEntryId);
|
|
const nextRows = rows.filter(
|
|
(row) => cleanString(row?.id) !== normalizedRowId
|
|
);
|
|
const deletedTimeEntryIds = removedTimeEntryId
|
|
? Array.from(
|
|
new Set([
|
|
...(appState.timeManagementModal.deletedTimeEntryIds || []),
|
|
removedTimeEntryId,
|
|
])
|
|
)
|
|
: appState.timeManagementModal.deletedTimeEntryIds || [];
|
|
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
timeEntryRows: nextRows,
|
|
selectedSrNumber: nextRows[0]?.selectedSrNumber || "",
|
|
selectedActivityValue: nextRows[0]?.selectedActivityValue || "",
|
|
selectedTaskTypeValue: nextRows[0]?.selectedTaskTypeValue || "",
|
|
highlightRowId: "",
|
|
deletedTimeEntryIds,
|
|
saveErrorMessage: "",
|
|
};
|
|
appState.pendingFocusSelector = getTimeManagementPostDeleteFocusSelector(
|
|
nextRows,
|
|
rows,
|
|
normalizedRowId
|
|
);
|
|
renderModal();
|
|
}
|
|
|
|
function getTimeManagementComboboxFocusSelector(rowId, field) {
|
|
return `.arch-panel-extension-combobox input[data-row-id="${cssEscape(rowId)}"][data-combobox-field="${cssEscape(field)}"]`;
|
|
}
|
|
|
|
function getTimeManagementPostDeleteFocusSelector(
|
|
nextRows,
|
|
previousRows,
|
|
removedRowId
|
|
) {
|
|
if (!nextRows.length) {
|
|
return "";
|
|
}
|
|
|
|
const removedIndex = previousRows.findIndex(
|
|
(row) => cleanString(row?.id) === cleanString(removedRowId)
|
|
);
|
|
const focusIndex = Math.min(Math.max(removedIndex, 0), nextRows.length - 1);
|
|
const focusRow = nextRows[focusIndex];
|
|
|
|
return focusRow?.id
|
|
? `.arch-panel-extension-time-management-delete[data-row-id="${cssEscape(focusRow.id)}"]`
|
|
: "";
|
|
}
|
|
|
|
function getTimeManagementRowById(modal, rowId) {
|
|
const rows = getTimeManagementRows(modal);
|
|
const normalizedRowId = cleanString(rowId);
|
|
|
|
return (
|
|
rows.find((row) => cleanString(row?.id) === normalizedRowId) ||
|
|
rows[0] ||
|
|
null
|
|
);
|
|
}
|
|
|
|
function updateTimeManagementRows(modal, rowId, nextRow) {
|
|
const rows = getTimeManagementRows(modal);
|
|
const normalizedRowId = cleanString(rowId || nextRow?.id);
|
|
|
|
return rows.map((row, index) => {
|
|
const isTarget = normalizedRowId
|
|
? cleanString(row?.id) === normalizedRowId
|
|
: index === 0;
|
|
|
|
return isTarget
|
|
? {
|
|
...row,
|
|
...nextRow,
|
|
id: row?.id || nextRow?.id || `row-${index + 1}`,
|
|
}
|
|
: row;
|
|
});
|
|
}
|
|
|
|
function updateTimeManagementSelectedSr(srNumber, rowId = "") {
|
|
if (!appState.timeManagementModal) {
|
|
return;
|
|
}
|
|
|
|
const targetRow = getTimeManagementRowById(appState.timeManagementModal, rowId);
|
|
const selectedServiceRequest = findTimeManagementServiceRequest(
|
|
appState.timeManagementModal,
|
|
srNumber
|
|
);
|
|
const selectedSrNumber =
|
|
selectedServiceRequest?.srNumber || cleanString(srNumber);
|
|
const duplicateRow = findDuplicateTimeManagementSrRow(
|
|
appState.timeManagementModal,
|
|
selectedSrNumber,
|
|
rowId
|
|
);
|
|
|
|
if (duplicateRow) {
|
|
highlightTimeManagementDuplicateRow(duplicateRow.id);
|
|
return;
|
|
}
|
|
|
|
const nextRow = {
|
|
...(targetRow || createBlankTimeManagementEntryRow()),
|
|
selectedSrNumber,
|
|
selectedActivityValue: "",
|
|
selectedTaskTypeValue: "",
|
|
};
|
|
const nextRows = updateTimeManagementRows(
|
|
appState.timeManagementModal,
|
|
rowId,
|
|
nextRow
|
|
);
|
|
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
timeEntryRows: nextRows,
|
|
selectedSrNumber: nextRows[0]?.selectedSrNumber || selectedSrNumber,
|
|
selectedActivityValue: nextRows[0]?.selectedActivityValue || "",
|
|
selectedTaskTypeValue: nextRows[0]?.selectedTaskTypeValue || "",
|
|
highlightRowId: "",
|
|
saveErrorMessage: "",
|
|
};
|
|
renderModal();
|
|
}
|
|
|
|
function updateTimeManagementField(fieldName, inputValue, rowId = "") {
|
|
if (!appState.timeManagementModal) {
|
|
return;
|
|
}
|
|
|
|
const targetRow = getTimeManagementRowById(appState.timeManagementModal, rowId);
|
|
const normalizedInput = cleanString(inputValue);
|
|
let value = normalizedInput;
|
|
|
|
if (fieldName === "selectedActivityValue") {
|
|
const options = getTimeManagementActivityOptions(
|
|
appState.timeManagementModal,
|
|
targetRow?.selectedSrNumber || appState.timeManagementModal.selectedSrNumber
|
|
);
|
|
value = findOptionValue(options, normalizedInput) || normalizedInput;
|
|
}
|
|
|
|
if (fieldName === "selectedTaskTypeValue") {
|
|
const options = getTimeManagementTaskTypeOptions(
|
|
appState.timeManagementModal,
|
|
targetRow?.selectedSrNumber || appState.timeManagementModal.selectedSrNumber
|
|
);
|
|
value = findOptionValue(options, normalizedInput) || normalizedInput;
|
|
|
|
const duplicateRow = findDuplicateTimeManagementNonServiceTaskTypeRow(
|
|
appState.timeManagementModal,
|
|
value,
|
|
rowId
|
|
);
|
|
|
|
if (duplicateRow) {
|
|
highlightTimeManagementDuplicateRow(duplicateRow.id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const nextRows = updateTimeManagementRows(appState.timeManagementModal, rowId, {
|
|
...(targetRow || createBlankTimeManagementEntryRow()),
|
|
[fieldName]: value,
|
|
});
|
|
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
timeEntryRows: nextRows,
|
|
[fieldName]: value,
|
|
highlightRowId: "",
|
|
saveErrorMessage: "",
|
|
};
|
|
}
|
|
|
|
function findDuplicateTimeManagementSrRow(modal, srNumber, currentRowId = "") {
|
|
const normalizedSrNumber = normalizeString(srNumber);
|
|
const normalizedCurrentRowId = cleanString(currentRowId);
|
|
|
|
if (!normalizedSrNumber || isNonServiceRequestSrNumber(srNumber)) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
getTimeManagementRows(modal).find((row) => {
|
|
return (
|
|
cleanString(row?.id) !== normalizedCurrentRowId &&
|
|
normalizeString(row?.selectedSrNumber) === normalizedSrNumber
|
|
);
|
|
}) || null
|
|
);
|
|
}
|
|
|
|
function findExistingTimeManagementDuplicateRow(modal, type) {
|
|
const seen = new Map();
|
|
|
|
for (const row of getTimeManagementRows(modal)) {
|
|
const rowId = cleanString(row?.id);
|
|
let key = "";
|
|
|
|
if (type === "non-service") {
|
|
if (!isNonServiceRequestSrNumber(row?.selectedSrNumber)) {
|
|
continue;
|
|
}
|
|
|
|
key = normalizeString(row?.selectedTaskTypeValue);
|
|
} else {
|
|
if (isNonServiceRequestSrNumber(row?.selectedSrNumber)) {
|
|
continue;
|
|
}
|
|
|
|
key = normalizeString(row?.selectedSrNumber);
|
|
}
|
|
|
|
if (!key) {
|
|
continue;
|
|
}
|
|
|
|
if (seen.has(key)) {
|
|
return (
|
|
getTimeManagementRows(modal).find(
|
|
(candidate) => cleanString(candidate?.id) === seen.get(key)
|
|
) || row
|
|
);
|
|
}
|
|
|
|
seen.set(key, rowId);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function findDuplicateTimeManagementNonServiceTaskTypeRow(
|
|
modal,
|
|
taskTypeValue,
|
|
currentRowId = ""
|
|
) {
|
|
const normalizedTaskTypeValue = normalizeString(taskTypeValue);
|
|
const normalizedCurrentRowId = cleanString(currentRowId);
|
|
|
|
if (!normalizedTaskTypeValue) {
|
|
return null;
|
|
}
|
|
|
|
const currentRow = getTimeManagementRowById(modal, currentRowId);
|
|
|
|
if (!isNonServiceRequestSrNumber(currentRow?.selectedSrNumber)) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
getTimeManagementRows(modal).find((row) => {
|
|
return (
|
|
cleanString(row?.id) !== normalizedCurrentRowId &&
|
|
isNonServiceRequestSrNumber(row?.selectedSrNumber) &&
|
|
normalizeString(row?.selectedTaskTypeValue) === normalizedTaskTypeValue
|
|
);
|
|
}) || null
|
|
);
|
|
}
|
|
|
|
function highlightTimeManagementDuplicateRow(rowId) {
|
|
const normalizedRowId = cleanString(rowId);
|
|
|
|
if (!appState.timeManagementModal || !normalizedRowId) {
|
|
return;
|
|
}
|
|
|
|
appState.pendingTimeManagementHighlightRowId = normalizedRowId;
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
highlightRowId: normalizedRowId,
|
|
};
|
|
renderModal();
|
|
}
|
|
|
|
function updateTimeManagementDayHours(rowId, dateKey, inputValue) {
|
|
if (!appState.timeManagementModal) {
|
|
return;
|
|
}
|
|
|
|
const targetRow = getTimeManagementRowById(appState.timeManagementModal, rowId);
|
|
const nextRow = {
|
|
...(targetRow || createBlankTimeManagementEntryRow()),
|
|
dayHours: {
|
|
...(targetRow?.dayHours || {}),
|
|
[cleanString(dateKey)]: cleanString(inputValue),
|
|
},
|
|
};
|
|
|
|
appState.timeManagementModal = {
|
|
...appState.timeManagementModal,
|
|
timeEntryRows: updateTimeManagementRows(
|
|
appState.timeManagementModal,
|
|
rowId,
|
|
nextRow
|
|
),
|
|
highlightRowId: "",
|
|
saveErrorMessage: "",
|
|
};
|
|
renderModal();
|
|
}
|
|
|
|
async function saveTimeManagementChanges() {
|
|
const modal = appState.timeManagementModal;
|
|
|
|
if (!modal || modal.isSaving || !isTimeManagementSaveEnabled(modal)) {
|
|
return;
|
|
}
|
|
|
|
appState.timeManagementModal = {
|
|
...modal,
|
|
isSaving: true,
|
|
saveErrorMessage: "",
|
|
};
|
|
renderModal();
|
|
|
|
try {
|
|
const resourceId = getCurrentUserResourcePartyId();
|
|
const weekId = cleanString(modal.weekId);
|
|
|
|
if (!resourceId) {
|
|
throw new Error("resourceCurrentUser.resourcePartyId nao esta disponivel.");
|
|
}
|
|
|
|
if (!weekId) {
|
|
throw new Error("WeekId nao encontrado para gravacao do apontamento.");
|
|
}
|
|
|
|
const changes = getTimeManagementPendingChanges(modal);
|
|
const baseUrl = COMCIP_REQUEST.timeEntriesUrl;
|
|
const updatedPayload = changes.updatedRows.map((row) =>
|
|
createTimeManagementSavePayload(row, modal.weekDays, weekId, resourceId, {
|
|
includeId: true,
|
|
})
|
|
);
|
|
const newPayload = changes.newRows.map((row) =>
|
|
createTimeManagementSavePayload(row, modal.weekDays, weekId, resourceId)
|
|
);
|
|
|
|
for (const timeEntryId of changes.deletedIds) {
|
|
await comcipRepository.mutateComcipPayload(
|
|
"DELETE",
|
|
`${baseUrl}/${encodeURIComponent(timeEntryId)}`,
|
|
null,
|
|
"timeEntryDelete"
|
|
);
|
|
}
|
|
|
|
if (updatedPayload.length) {
|
|
await comcipRepository.mutateComcipPayload(
|
|
"PUT",
|
|
baseUrl,
|
|
updatedPayload,
|
|
"timeEntryUpdate"
|
|
);
|
|
}
|
|
|
|
if (newPayload.length) {
|
|
await comcipRepository.mutateComcipPayload(
|
|
"POST",
|
|
baseUrl,
|
|
newPayload,
|
|
"timeEntryCreate"
|
|
);
|
|
}
|
|
|
|
await loadTimeManagementData(
|
|
modal.selectedDate || getDateInputValue(new Date())
|
|
);
|
|
} catch (error) {
|
|
appState.timeManagementModal = {
|
|
...(appState.timeManagementModal || modal),
|
|
isSaving: false,
|
|
saveErrorMessage: getErrorMessage(error),
|
|
};
|
|
renderModal();
|
|
}
|
|
}
|
|
|
|
function selectTimeManagementComboboxOption(field, value, rowId = "") {
|
|
if (field === "sr") {
|
|
updateTimeManagementSelectedSr(value || "", rowId);
|
|
return;
|
|
}
|
|
|
|
if (field === "activity") {
|
|
updateTimeManagementField("selectedActivityValue", value || "", rowId);
|
|
renderModal();
|
|
return;
|
|
}
|
|
|
|
if (field === "taskType") {
|
|
updateTimeManagementField("selectedTaskTypeValue", value || "", rowId);
|
|
renderModal();
|
|
}
|
|
}
|
|
|
|
function filterTimeManagementCombobox(input) {
|
|
const query = normalizeString(input.value);
|
|
const combobox = input.closest(".arch-panel-extension-combobox");
|
|
|
|
if (!combobox) {
|
|
return;
|
|
}
|
|
|
|
const options = Array.from(
|
|
combobox.querySelectorAll(".arch-panel-extension-combobox-option")
|
|
);
|
|
|
|
for (const option of options) {
|
|
const searchText = normalizeString(option.getAttribute("data-search") || "");
|
|
option.hidden = Boolean(query) && !searchText.includes(query);
|
|
}
|
|
|
|
setActiveTimeManagementComboboxOption(
|
|
combobox,
|
|
getVisibleTimeManagementComboboxOptions(combobox).find((option) =>
|
|
option.classList.contains("is-selected")
|
|
) || getVisibleTimeManagementComboboxOptions(combobox)[0]
|
|
);
|
|
}
|
|
|
|
function handleTimeManagementComboboxKeydown(event, input) {
|
|
const combobox = input.closest(".arch-panel-extension-combobox");
|
|
|
|
if (!combobox) {
|
|
return;
|
|
}
|
|
|
|
if (event.key === "Escape") {
|
|
clearActiveTimeManagementComboboxOption(combobox);
|
|
input.blur();
|
|
return;
|
|
}
|
|
|
|
const visibleOptions = getVisibleTimeManagementComboboxOptions(combobox);
|
|
|
|
if (!visibleOptions.length) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
if (event.key === "Enter") {
|
|
const activeOption =
|
|
visibleOptions.find((option) => option.classList.contains("is-active")) ||
|
|
visibleOptions[0];
|
|
activeOption.click();
|
|
return;
|
|
}
|
|
|
|
const activeIndex = visibleOptions.findIndex((option) =>
|
|
option.classList.contains("is-active")
|
|
);
|
|
const selectedIndex = visibleOptions.findIndex((option) =>
|
|
option.classList.contains("is-selected")
|
|
);
|
|
const baseIndex = activeIndex >= 0 ? activeIndex : selectedIndex;
|
|
let nextIndex = 0;
|
|
|
|
if (event.key === "ArrowDown") {
|
|
nextIndex = baseIndex >= 0 ? baseIndex + 1 : 0;
|
|
} else if (event.key === "ArrowUp") {
|
|
nextIndex = baseIndex >= 0 ? baseIndex - 1 : visibleOptions.length - 1;
|
|
} else if (event.key === "Home") {
|
|
nextIndex = 0;
|
|
} else if (event.key === "End") {
|
|
nextIndex = visibleOptions.length - 1;
|
|
}
|
|
|
|
if (nextIndex < 0) {
|
|
nextIndex = visibleOptions.length - 1;
|
|
} else if (nextIndex >= visibleOptions.length) {
|
|
nextIndex = 0;
|
|
}
|
|
|
|
setActiveTimeManagementComboboxOption(combobox, visibleOptions[nextIndex]);
|
|
}
|
|
|
|
function getVisibleTimeManagementComboboxOptions(combobox) {
|
|
return Array.from(
|
|
combobox.querySelectorAll(".arch-panel-extension-combobox-option")
|
|
).filter((option) => !option.hidden);
|
|
}
|
|
|
|
function setActiveTimeManagementComboboxOption(combobox, option) {
|
|
clearActiveTimeManagementComboboxOption(combobox);
|
|
|
|
if (!option) {
|
|
return;
|
|
}
|
|
|
|
option.classList.add("is-active");
|
|
combobox
|
|
.querySelector("input[data-combobox-field]")
|
|
?.setAttribute("aria-activedescendant", option.id || "");
|
|
option.scrollIntoView({ block: "nearest" });
|
|
}
|
|
|
|
function clearActiveTimeManagementComboboxOption(combobox) {
|
|
combobox
|
|
.querySelectorAll(".arch-panel-extension-combobox-option.is-active")
|
|
.forEach((option) => option.classList.remove("is-active"));
|
|
combobox
|
|
.querySelector("input[data-combobox-field]")
|
|
?.removeAttribute("aria-activedescendant");
|
|
}
|
|
|
|
async function fetchCachedActivityRequest(srNumbers) {
|
|
const cacheKey = getActivityRequestCacheKey(srNumbers);
|
|
|
|
if (!cacheKey) {
|
|
return {
|
|
url: "",
|
|
payload: null,
|
|
};
|
|
}
|
|
|
|
const cachedRequest = appState.timeManagementActivityCache.get(cacheKey);
|
|
|
|
if (cachedRequest?.payload) {
|
|
return cachedRequest;
|
|
}
|
|
|
|
if (cachedRequest?.promise) {
|
|
return cachedRequest.promise;
|
|
}
|
|
|
|
const requestPromise = comcipRepository
|
|
.fetchActivityRequest(srNumbers)
|
|
.then((result) => {
|
|
appState.timeManagementActivityCache.set(cacheKey, {
|
|
url: result.url,
|
|
payload: result.payload,
|
|
});
|
|
|
|
return result;
|
|
})
|
|
.catch((error) => {
|
|
appState.timeManagementActivityCache.delete(cacheKey);
|
|
throw error;
|
|
});
|
|
|
|
appState.timeManagementActivityCache.set(cacheKey, {
|
|
promise: requestPromise,
|
|
});
|
|
|
|
return requestPromise;
|
|
}
|
|
|
|
function getActivityRequestCacheKey(srNumbers) {
|
|
const normalizedSrNumbers = (srNumbers || [])
|
|
.map((srNumber) => cleanString(srNumber).toUpperCase())
|
|
.filter((srNumber) => /^SR\d+$/i.test(srNumber))
|
|
.sort();
|
|
|
|
return normalizedSrNumbers.join("|");
|
|
}
|
|
|
|
async function fetchTaskTypeRequest() {
|
|
if (appState.taskTypeCache.promise) {
|
|
return appState.taskTypeCache.promise;
|
|
}
|
|
|
|
appState.taskTypeCache.promise = (async () => {
|
|
return readCachedTaskTypePayload().catch(() => null);
|
|
})().finally(() => {
|
|
appState.taskTypeCache.promise = null;
|
|
});
|
|
|
|
return appState.taskTypeCache.promise;
|
|
}
|
|
|
|
async function refreshTaskTypeCache() {
|
|
const payload = await comcipRepository.fetchTaskTypePayload();
|
|
await writeCachedTaskTypePayload(payload);
|
|
|
|
return payload;
|
|
}
|
|
|
|
function getCurrentUserResourcePartyId() {
|
|
const resourceCurrentUser = appState.dataset?.resourceCurrentUser;
|
|
const directResourcePartyId = cleanString(
|
|
getFieldValue(resourceCurrentUser, [
|
|
"resourcePartyId",
|
|
"ResourcePartyId",
|
|
"ResourcePartyID",
|
|
"partyId",
|
|
"PartyId",
|
|
])
|
|
);
|
|
|
|
if (directResourcePartyId) {
|
|
return directResourcePartyId;
|
|
}
|
|
|
|
const candidates = Array.isArray(resourceCurrentUser)
|
|
? resourceCurrentUser
|
|
: extractArray(resourceCurrentUser, ["items", "data", "results", "content"]);
|
|
const source = candidates[0] || resourceCurrentUser;
|
|
|
|
return cleanString(
|
|
getFieldValue(source, [
|
|
"resourcePartyId",
|
|
"ResourcePartyId",
|
|
"ResourcePartyID",
|
|
"partyId",
|
|
"PartyId",
|
|
])
|
|
);
|
|
}
|
|
|
|
return Object.freeze({
|
|
toggleTimeManagementDatePicker,
|
|
shiftTimeManagementDatePickerMonth,
|
|
selectTimeManagementDate,
|
|
shiftTimeManagementDateByWeeks,
|
|
openTimeManagementModal,
|
|
loadTimeManagementData,
|
|
addTimeManagementEntryRow,
|
|
removeTimeManagementEntryRow,
|
|
updateTimeManagementSelectedSr,
|
|
updateTimeManagementField,
|
|
updateTimeManagementDayHours,
|
|
saveTimeManagementChanges,
|
|
selectTimeManagementComboboxOption,
|
|
filterTimeManagementCombobox,
|
|
handleTimeManagementComboboxKeydown,
|
|
refreshTaskTypeCache,
|
|
});
|
|
}
|
|
|
|
globalThis.ArchPanelManageTimeController = Object.freeze({
|
|
create,
|
|
});
|
|
})();
|