diff --git a/coretabs.user.js b/coretabs.user.js index 3c8065d..4aecdbe 100644 --- a/coretabs.user.js +++ b/coretabs.user.js @@ -48,11 +48,14 @@ let allMyCases = [], allCaseDocuments = [], allCaseUsers = [], - refundReviewData = []; - let filteredRefundData = []; - let selectedCaseId = null; - let loadedDocsForCaseId = null; - let loadedUsersForCaseId = null; + caseHistoryData = [], + caseSubProcessData = {}, + refundReviewData = [], + filteredRefundData = [], + selectedCaseId = null, + loadedDocsForCaseId = null, + loadedUsersForCaseId = null, + loadedHistoryForCaseId = null; function addStyles() { GM_addStyle(` @@ -109,12 +112,20 @@ +

Loading my cases...

Please select a case to view its documents.

Please select a case to view its users.

+
+
+
+ +
+

Please select a case to view its history.

+
@@ -298,6 +309,118 @@ (responseArea.innerHTML = ""), responseArea.appendChild(table)); } + function renderCaseHistoryTable() { + const responseArea = document.querySelector( + "#tab-history .results-container", + ); + if (!caseHistoryData || caseHistoryData.length === 0) { + responseArea.innerHTML = + '

No history data available for this case.

'; + return; + } + + const filterValue = document.getElementById("history-type-filter").value; + const filteredHistory = caseHistoryData.filter( + (item) => filterValue === "all" || item.CaseType === filterValue, + ); + + if (filteredHistory.length === 0) { + responseArea.innerHTML = + '

No history items match the selected filter.

'; + return; + } + + const table = createTable([ + "Routing Date", + "Performed By", + "Workflow Step", + ]), + tbody = document.createElement("tbody"); + + // Group items by CaseType + const groupedHistory = {}; + filteredHistory.forEach((item) => { + const caseType = item.CaseType || "Unknown"; + if (!groupedHistory[caseType]) { + groupedHistory[caseType] = []; + } + groupedHistory[caseType].push(item); + }); + + // Create table rows with grouping + Object.keys(groupedHistory).forEach((caseType) => { + const groupItems = groupedHistory[caseType]; + + // Create group header row + const sanitizedCaseType = caseType.replace(/[^a-zA-Z0-9]/g, "_"); + tbody.innerHTML += `${caseType} (${groupItems.length} items)`; + + // Add current role type at the top of each group + // Find the most recent item (with latest RoutingDate) + const mostRecentItem = groupItems.reduce((latest, current) => { + const latestDate = new Date(latest.RoutingDate); + const currentDate = new Date(current.RoutingDate); + return currentDate > latestDate ? current : latest; + }); + const currentCaseRoleTypeCode = mostRecentItem.ToWorkflowStepIdentifier + ? caseSubProcessData[mostRecentItem.ToWorkflowStepIdentifier] + : null; + + // Always add the current role row, even if CaseRoleTypeCode is not available + const currentRoleRow = document.createElement("tr"); + currentRoleRow.className = `group-member history-group-${sanitizedCaseType}`; + + // Empty Routing Date + const emptyDateCell = document.createElement("td"); + emptyDateCell.textContent = ""; + currentRoleRow.appendChild(emptyDateCell); + + // CaseRoleTypeCode in Performed By column + const roleCell = document.createElement("td"); + if (currentCaseRoleTypeCode) { + roleCell.innerHTML = `Current Role:
${currentCaseRoleTypeCode}`; + } else { + roleCell.innerHTML = `Current Role:
Not available`; + } + currentRoleRow.appendChild(roleCell); + + // Last To Step in Workflow Step column + const workflowStepCell = document.createElement("td"); + workflowStepCell.textContent = mostRecentItem.ToWorkflowStep || "-"; + currentRoleRow.appendChild(workflowStepCell); + + tbody.appendChild(currentRoleRow); + + // Create member rows for this group + groupItems.forEach((item) => { + const row = document.createElement("tr"); + row.className = `group-member history-group-${sanitizedCaseType}`; + + // Format RoutingDate + const routingDate = new Date(item.RoutingDate); + const dateCell = document.createElement("td"); + dateCell.textContent = routingDate.toLocaleString("id-ID"); + row.appendChild(dateCell); + + // PerformedByUser + const performedByCell = document.createElement("td"); + performedByCell.textContent = item.PerformedByUser || "-"; + row.appendChild(performedByCell); + + // FromWorkflowStep (renamed to Workflow Step) + const workflowStepCell = document.createElement("td"); + workflowStepCell.textContent = item.FromWorkflowStep || "-"; + row.appendChild(workflowStepCell); + + tbody.appendChild(row); + }); + }); + + responseArea.innerHTML = ""; + table.appendChild(tbody); + responseArea.appendChild(table); + tbody.addEventListener("click", handleGroupToggle); + } function renderRefundReviewTable() { const responseArea = document.querySelector( "#tab-refund .results-container", @@ -481,6 +604,91 @@ handleError(error, responseArea); } } + async function fetchCaseHistory(caseId) { + const responseArea = document.querySelector( + "#tab-history .results-container", + ); + responseArea.innerHTML = + '

Loading history...

'; + try { + if (!caseId) throw new Error("No Case ID provided."); + const apiUrl = + "https://coretax.intranet.pajak.go.id/casemanagement/api/caseroutinghistory/list", + payload = { + AggregateIdentifier: caseId, + First: 0, + Rows: 1000, + SortField: "RoutingDate", + SortOrder: -1, + Filters: [], + LanguageId: "id-ID", + }, + fetchOptions = { + method: "POST", + headers: getHeaders(caseId), + body: JSON.stringify(payload), + }, + response = await fetch(apiUrl, fetchOptions); + if (!response.ok) { + const errorData = await response.json(); + throw new Error( + `API Error: ${errorData.Message || response.statusText}`, + ); + } + const data = await response.json(); + caseHistoryData = data?.Payload?.Data || []; + loadedHistoryForCaseId = caseId; + + // Fetch CaseRoleTypeCode for each ToWorkflowStepIdentifier + await fetchCaseSubProcessData(caseId); + + populateFilter("history-type-filter", caseHistoryData, "CaseType"); + renderCaseHistoryTable(); + } catch (error) { + handleError(error, responseArea); + } + } + async function fetchCaseSubProcessData(caseId) { + try { + const apiUrl = + "https://coretax.intranet.pajak.go.id/casemanagement/api/casesubprocess/list", + payload = { + First: 0, + Rows: 10000, + Filters: [], + AggregateIdentifier: caseId, + }, + fetchOptions = { + method: "POST", + headers: getHeaders(caseId), + body: JSON.stringify(payload), + }, + response = await fetch(apiUrl, fetchOptions); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error( + `Case SubProcess API Error: ${errorData.Message || response.statusText}`, + ); + } + + const data = await response.json(); + const subProcessData = data?.Payload?.Data || []; + + // Create a lookup map for WorkflowStepIdentifier to CaseRoleTypeCode + caseSubProcessData = {}; + subProcessData.forEach((item) => { + if (item.WorkflowStepIdentifier && item.CaseRoleTypeCode) { + caseSubProcessData[item.WorkflowStepIdentifier] = + item.CaseRoleTypeCode; + } + }); + } catch (error) { + console.error("Error fetching case subprocess data:", error); + caseSubProcessData = {}; + } + } + async function downloadDocument(docId, filename, button) { const originalText = button.textContent; ((button.textContent = "Downloading..."), (button.disabled = !0)); @@ -673,11 +881,9 @@ const table = document.createElement("table"); table.className = "ct-results-table"; const thead = document.createElement("thead"); - return ( - (thead.innerHTML = `${headers.map((e) => `${e}`).join("")}`), - table.appendChild(thead), - table - ); + thead.innerHTML = `${headers.map((e) => `${e}`).join("")}`; + table.appendChild(thead); + return table; } function handleError(error, area) { console.error("Userscript Error:", error); @@ -823,7 +1029,7 @@ headerRow.classList.toggle("expanded"); const isCollapsed = headerRow.classList.contains("collapsed"); const tableBody = headerRow.parentElement; - const memberRows = tableBody.querySelectorAll(`.group-member.${groupId}`); + const memberRows = tableBody.querySelectorAll(`tr.group-member.${groupId}`); memberRows.forEach((row) => { row.style.display = isCollapsed ? "none" : "table-row"; }); @@ -837,14 +1043,18 @@ .forEach((e) => e.classList.remove("active")), document.querySelector(`[data-tab="${tabId}"]`).classList.add("active"), document.getElementById(tabId).classList.add("active"), - "tab-docs" === tabId - ? selectedCaseId && - selectedCaseId !== loadedDocsForCaseId && - fetchCaseDocuments(selectedCaseId) - : "tab-users" === tabId && - selectedCaseId && - selectedCaseId !== loadedUsersForCaseId && - fetchCaseUsers(selectedCaseId)); + "tab-docs" === tabId && + selectedCaseId && + selectedCaseId !== loadedDocsForCaseId && + fetchCaseDocuments(selectedCaseId), + "tab-users" === tabId && + selectedCaseId && + selectedCaseId !== loadedUsersForCaseId && + fetchCaseUsers(selectedCaseId), + "tab-history" === tabId && + selectedCaseId && + selectedCaseId !== loadedHistoryForCaseId && + fetchCaseHistory(selectedCaseId)); } function downloadRefundExcel() { if (!filteredRefundData || filteredRefundData.length === 0) { @@ -992,6 +1202,10 @@ if (usersFilterEl) usersFilterEl.addEventListener("change", renderCaseUsersTable); + const historyFilterEl = document.getElementById("history-type-filter"); + if (historyFilterEl) + historyFilterEl.addEventListener("change", renderCaseHistoryTable); + const refundFilterEl = document.getElementById("refund-reported-filter"); if (refundFilterEl) refundFilterEl.addEventListener("change", renderRefundReviewTable); @@ -1018,6 +1232,12 @@ toggleAllGroups("#tab-refund .results-container", e.target), ); + const toggleHistoryBtn = document.getElementById("toggle-history-btn"); + if (toggleHistoryBtn) + toggleHistoryBtn.addEventListener("click", (e) => + toggleAllGroups("#tab-history .results-container", e.target), + ); + // initial load fetchMyCases(); }