add-new-case-history-tab #1

Merged
diasbaskara merged 3 commits from add-new-case-history-tab into main 2025-11-26 09:12:20 +00:00
Showing only changes of commit 50401a73c9 - Show all commits

View File

@@ -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 @@
<button class="ct-tab-button active" data-tab="tab-my-cases">My Cases</button>
<button class="ct-tab-button" data-tab="tab-docs">Case Documents</button>
<button class="ct-tab-button" data-tab="tab-users">Case Users</button>
<button class="ct-tab-button" data-tab="tab-history">Case History</button>
<button class="ct-tab-button" data-tab="tab-refund">Refund Review</button>
</div>
<div id="ct-tab-content-area">
<div id="tab-my-cases" class="ct-tab-panel active"><div class="filter-container"><div><label for="cases-status-filter">Filter by Status:</label><select id="cases-status-filter"></select></div><button id="toggle-cases-btn" class="action-btn" style="margin-left:auto">Collapse All</button></div><div class="results-container"><p style="padding:15px;color:#666;">Loading my cases...</p></div></div>
<div id="tab-docs" class="ct-tab-panel"><div class="filter-container"><div><label for="docs-status-filter">Filter by Status:</label><select id="docs-status-filter"></select></div><button id="toggle-docs-btn" class="action-btn" style="margin-left:auto">Collapse All</button></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case to view its documents.</p></div></div>
<div id="tab-users" class="ct-tab-panel"><div class="filter-container"><div><label for="users-role-filter">Filter by Role:</label><select id="users-role-filter"></select></div></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case to view its users.</p></div></div>
<div id="tab-history" class="ct-tab-panel">
<div class="filter-container">
<div><label for="history-type-filter">Filter by Case Type:</label><select id="history-type-filter"></select></div>
<button id="toggle-history-btn" class="action-btn" style="margin-left:auto">Collapse All</button>
</div>
<div class="results-container"><p style="padding:15px;color:#666;">Please select a case to view its history.</p></div>
</div>
<div id="tab-refund" class="ct-tab-panel">
<div class="filter-container">
<div><label for="refund-reported-filter">Filter by Reported:</label><select id="refund-reported-filter"></select></div>
@@ -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 =
'<p style="padding:15px;color:#666;">No history data available for this case.</p>';
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 =
'<p style="padding:15px;color:#666;">No history items match the selected filter.</p>';
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 += `<tr class="group-header expanded" data-group-id="history-group-${sanitizedCaseType}"><td colspan="3"><span class="toggle-icon"></span>${caseType} (${groupItems.length} items)</td></tr>`;
// 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 = `<strong>Current Role:</strong><br><small style="color: #666;">${currentCaseRoleTypeCode}</small>`;
} else {
roleCell.innerHTML = `<strong>Current Role:</strong><br><small style="color: #666; font-style: italic;">Not available</small>`;
}
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 =
'<p style="padding:15px;color:#666;">Loading history...</p>';
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 = `<tr>${headers.map((e) => `<th>${e}</th>`).join("")}</tr>`),
table.appendChild(thead),
table
);
thead.innerHTML = `<tr>${headers.map((e) => `<th>${e}</th>`).join("")}</tr>`;
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();
}