<div class="split-wrapper">
<!-- Left Flowchart Panel (Side 1) -->
<div class="panel panel-side-1">
<h3 class="panel-title panel-title-1" id='{ProcessID1}'>{ProcessName1}</h3>
<div id="apriso-flow-canvas-1" class="flow-canvas">
<svg id="apriso-flow-svg-1" height="2000">
<marker id="arrow-1" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L10,3 L0,6 Z" fill="#555"/>
<div class="fixed-authorize-container">
<i class="fa-brands fa-creative-commons-by authorize-icon"></i>
<button class="authorize-btn flowchart1btn" onclick="openAuthorizePopup(1)">Authorize</button>
<!-- Middle Comparison Area -->
<div class="middle-panel-container">
<!-- Left Flowchart Details (Side 1) -->
<div id="panel-details-1" class="side-panel side-panel-1" style="display:none;">
<div class="popup-header header-1">
<span class="panelTitle">Process Details</span>
<span class="close" onclick="closeSidePanel(1)">✖</span>
<div class="operation-view">
<li class="ctqclass hiddenclass"><span class="folder">🎯 Operation CTQ</span><div class="ctqContainer"></div></li>
<li class="stepsclass"><span class="folder">📋 Steps</span><ul class="stepList"></ul></li>
<li class="charclass"><span class="folder">📐 Characteristics</span><ul class="charList"></ul></li>
<li class="compclass"><span class="folder">🧩 Components</span><ul class="componentList"></ul></li>
<li class="docclass"><span class="folder">📄 Documents</span><ul class="documentList"></ul></li>
<li class="wcclass"><span class="folder">🏭 Work Centers</span><ul class="workcenterList"></ul></li>
<li class="resclass"><span class="folder">🔧 Resources</span><ul class="resourceList"></ul></li>
<!-- Right Flowchart Details (Side 2) -->
<div id="panel-details-2" class="side-panel side-panel-2" style="display:none;">
<div class="popup-header header-2">
<span class="panelTitle">Process Details</span>
<span class="close" onclick="closeSidePanel(2)">✖</span>
<div class="operation-view">
<li class="ctqclass hiddenclass"><span class="folder">🎯 Operation CTQ</span><div class="ctqContainer"></div></li>
<li class="stepsclass"><span class="folder">📋 Steps</span><ul class="stepList"></ul></li>
<li class="charclass"><span class="folder">📐 Characteristics</span><ul class="charList"></ul></li>
<li class="compclass"><span class="folder">🧩 Components</span><ul class="componentList"></ul></li>
<li class="docclass"><span class="folder">📄 Documents</span><ul class="documentList"></ul></li>
<li class="wcclass"><span class="folder">🏭 Work Centers</span><ul class="workcenterList"></ul></li>
<li class="resclass"><span class="folder">🔧 Resources</span><ul class="resourceList"></ul></li>
<!-- Right Flowchart Panel (Side 2) -->
<div class="panel panel-side-2">
<h3 class="panel-title panel-title-2" id='{ProcessID2}'>{ProcessName2}</h3>
<div id="apriso-flow-canvas-2" class="flow-canvas">
<svg id="apriso-flow-svg-2" height="2000">
<marker id="arrow-2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L10,3 L0,6 Z" fill="#555"/>
<div class="fixed-authorize-container">
<i class="fa-brands fa-creative-commons-by authorize-icon"></i>
<button class="authorize-btn flowchart2btn" onclick="openAuthorizePopup(2)">Authorize</button>
<!-- Center Authorization Modal Popup -->
<div id="authorize-modal" class="auth-modal-overlay" style="display:none;">
<div class="auth-modal-content">
<div class="auth-modal-header">
<span id="auth-modal-heading">Process Name</span>
<span class="close" onclick="closeAuthorizePopup()">✖</span>
<div class="auth-modal-body">
<div class="auth-form-row">
<label class="auth-label" for="auth-effective-date">Effective date:</label>
<!-- Custom Picker Wrapper -->
<div class="custom-picker-wrapper">
<input type="text" id="auth-effective-date" class="apr-input effectivedate" readonly placeholder="dd-mm-yyyy --:--" onclick="openCustomPicker()" />
<!-- CALENDAR POPUP WITH OK & CANCEL -->
<div id="custom-picker-dropdown" class="custom-picker-dropdown" style="display:none;">
<div class="picker-top-bar">
<span id="picker-month-year" class="month-year-title">July, 2026</span>
<div class="month-nav-btns">
<button type="button" onclick="changeMonth(-1)">↑</button>
<button type="button" onclick="changeMonth(1)">↓</button>
<div class="picker-main-body">
<!-- Calendar Section -->
<div class="calendar-section">
<span>Su</span><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span>
<div id="calendar-days" class="days-grid"></div>
<!-- Native-Style Vertical Time Wheel Column -->
<div class="time-wheels-container">
<div id="wheel-hours" class="time-wheel"></div>
<div id="wheel-minutes" class="time-wheel"></div>
<div class="picker-bottom-bar">
<div class="quick-links">
<span onclick="clearPickerSelection()">Clear</span>
<span onclick="selectToday()">Today</span>
<div class="picker-action-btns">
<button type="button" class="btn-picker-cancel" onclick="cancelPickerSelection()">Cancel</button>
<button type="button" class="btn-picker-ok" onclick="confirmPickerSelection()">OK</button>
<div class="auth-modal-footer">
<button type="button" class="auth-cancel-btn" onclick="closeAuthorizePopup()">Cancel</button>
<button id="auth-save-btn" class="auth-save-btn" onclick="saveAuthorization()" disabled>Save</button>
var currentcontext = $Context;
var currentActiveFlowchartName = "";
var currentactiveFlowchartId = 0;
var cachedDataFlowA = [];
var cachedDataFlowB = [];
// Custom Calendar State Tracking
var committedDateTime = "";
var tempSelectedDate = new Date();
$View.onLoaded(function () {
cachedDataFlowA = JSON.parse(currentcontext.inputs.Process1Details || "[]");
console.error("Invalid Process JSON A", e);
cachedDataFlowB = JSON.parse(currentcontext.inputs.Process2Details || "[]");
console.error("Invalid Process JSON B", e);
var Process1AuthAllow= currentcontext.inputs.Process1AuthAllow;
var Process2AuthAllow= currentcontext.inputs.Process2AuthAllow;
$(".flowchart1btn").prop("disabled",false);
$(".flowchart1btn").prop("disabled",true);
$(".flowchart2btn").prop("disabled",false);
$(".flowchart2btn").prop("disabled",true);
waitForRender(cachedDataFlowA, cachedDataFlowB);
window.addEventListener('resize', function() {
</script>
/* =========================================================
2. AUTHORIZATION POPUP & MODAL HANDLERS
========================================================= */
function openAuthorizePopup(side) {
const headingElem = document.getElementsByClassName("panel-title-" + side)[0];
currentActiveFlowchartName = headingElem ? headingElem.innerText.trim() : "Process " + side;
const modalHeading = document.getElementById("auth-modal-heading");
modalHeading.innerText = currentActiveFlowchartName;
// Reset date input state & hide calendar popup
const dateInput = document.getElementById("auth-effective-date") || document.querySelector(".effectivedate");
const dropdown = document.getElementById("custom-picker-dropdown") || document.querySelector(".custom-picker-dropdown");
const modal = document.getElementById("authorize-modal");
function closeAuthorizePopup() {
const dropdown = document.getElementById("custom-picker-dropdown") || document.querySelector(".custom-picker-dropdown");
const modal = document.getElementById("authorize-modal");
function toggleSaveButton() {
var saveBtn = document.getElementById("auth-save-btn") || document.querySelector(".auth-save-btn");
saveBtn.disabled = !committedDateTime;
function saveAuthorization() {
call_PCV20_AuthorizeProcess(currentcontext, currentactiveFlowchartId, committedDateTime);
/* =========================================================
3. CUSTOM CALENDAR POPUP FUNCTIONS
========================================================= */
function enforceFutureTime() {
if (tempSelectedDate < now) {
tempSelectedDate = new Date();
function openCustomPicker() {
var dropdown = document.getElementById("custom-picker-dropdown") || document.querySelector(".custom-picker-dropdown");
tempSelectedDate = new Date(committedDateTime);
tempSelectedDate = new Date();
function updateInputPreview() {
if (!tempSelectedDate) return;
var year = tempSelectedDate.getFullYear();
var month = String(tempSelectedDate.getMonth() + 1).padStart(2, '0');
var day = String(tempSelectedDate.getDate()).padStart(2, '0');
var hours = String(tempSelectedDate.getHours()).padStart(2, '0');
var mins = String(tempSelectedDate.getMinutes()).padStart(2, '0');
var dateInput = document.getElementById("auth-effective-date") || document.querySelector(".effectivedate");
dateInput.value = ${day}-${month}-${year} ${hours}:${mins};
function changeMonth(delta) {
var targetDate = new Date(tempSelectedDate.getFullYear(), tempSelectedDate.getMonth() + delta, 1);
var currentMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);
if (targetDate < currentMonthStart) {
tempSelectedDate.setMonth(tempSelectedDate.getMonth() + delta);
function renderCalendar() {
var year = tempSelectedDate.getFullYear();
var month = tempSelectedDate.getMonth();
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var monthYearElem = document.getElementById("picker-month-year");
monthYearElem.innerText = monthNames[month] + ", " + year;
var daysContainer = document.getElementById("calendar-days");
if (!daysContainer) return;
daysContainer.innerHTML = "";
var firstDay = new Date(year, month, 1).getDay();
var daysInMonth = new Date(year, month + 1, 0).getDate();
var todayMidnight = new Date(today.getFullYear(), today.getMonth(), today.getDate());
for (var i = 0; i < firstDay; i++) {
var emptyDiv = document.createElement("div");
emptyDiv.className = "day empty";
daysContainer.appendChild(emptyDiv);
for (var day = 1; day <= daysInMonth; day++) {
var dayDiv = document.createElement("div");
dayDiv.className = "day";
var currentDateMidnight = new Date(year, month, d);
if (tempSelectedDate.getDate() === d && tempSelectedDate.getMonth() === month && tempSelectedDate.getFullYear() === year) {
dayDiv.classList.add("selected");
if (todayMidnight.getTime() === currentDateMidnight.getTime()) {
dayDiv.classList.add("today-highlight");
if (currentDateMidnight < todayMidnight) {
dayDiv.classList.add("disabled-day");
dayDiv.onclick = function() {
tempSelectedDate.setDate(d);
daysContainer.appendChild(dayDiv);
function clearPickerSelection() {
tempSelectedDate = new Date();
var dateInput = document.getElementById("auth-effective-date") || document.querySelector(".effectivedate");
function renderTimeWheels() {
var hoursWheel = document.getElementById("wheel-hours");
var minsWheel = document.getElementById("wheel-minutes");
if (!hoursWheel || !minsWheel) return;
hoursWheel.innerHTML = "";
minsWheel.innerHTML = "";
tempSelectedDate.getFullYear() === now.getFullYear() &&
tempSelectedDate.getMonth() === now.getMonth() &&
tempSelectedDate.getDate() === now.getDate()
var currentHour = now.getHours();
var currentMin = now.getMinutes();
for (var h = 0; h < 24; h++) {
var item = document.createElement("div");
var formatted = hourVal < 10 ? "0" + hourVal : hourVal;
var isPastHour = isToday && (hourVal < currentHour);
var isSelected = (tempSelectedDate.getHours() === hourVal);
item.className = "wheel-item" +
(isSelected ? " selected" : "") +
(isPastHour ? " disabled-time" : "");
item.innerText = formatted;
item.onclick = function() {
tempSelectedDate.setHours(hourVal);
hoursWheel.appendChild(item);
for (var m = 0; m < 60; m++) {
var item = document.createElement("div");
var formatted = minVal < 10 ? "0" + minVal : minVal;
var isPastMin = isToday && (tempSelectedDate.getHours() === currentHour) && (minVal < currentMin);
var isSelected = (tempSelectedDate.getMinutes() === minVal);
item.className = "wheel-item" +
(isSelected ? " selected" : "") +
(isPastMin ? " disabled-time" : "");
item.innerText = formatted;
item.onclick = function() {
tempSelectedDate.setMinutes(minVal);
minsWheel.appendChild(item);
tempSelectedDate = new Date();
function confirmPickerSelection() {
var year = tempSelectedDate.getFullYear();
var month = String(tempSelectedDate.getMonth() + 1).padStart(2, '0');
var day = String(tempSelectedDate.getDate()).padStart(2, '0');
var hours = String(tempSelectedDate.getHours()).padStart(2, '0');
var mins = String(tempSelectedDate.getMinutes()).padStart(2, '0');
committedDateTime = ${year}-${month}-${day}T${hours}:${mins};
var dateInput = document.getElementById("auth-effective-date") || document.querySelector(".effectivedate");
dateInput.value = ${day}-${month}-${year} ${hours}:${mins};
var dropdown = document.getElementById("custom-picker-dropdown") || document.querySelector(".custom-picker-dropdown");
function cancelPickerSelection() {
var dateInput = document.getElementById("auth-effective-date") || document.querySelector(".effectivedate");
var d = new Date(committedDateTime);
var year = d.getFullYear();
var month = String(d.getMonth() + 1).padStart(2, '0');
var day = String(d.getDate()).padStart(2, '0');
var hours = String(d.getHours()).padStart(2, '0');
var mins = String(d.getMinutes()).padStart(2, '0');
dateInput.value = ${day}-${month}-${year} ${hours}:${mins};
var dropdown = document.getElementById("custom-picker-dropdown") || document.querySelector(".custom-picker-dropdown");
/* =========================================================
========================================================= */
function redrawAllFlowcharts() {
const c1 = document.getElementById("apriso-flow-canvas-1");
const c2 = document.getElementById("apriso-flow-canvas-2");
const s1 = document.getElementById("apriso-flow-svg-1");
const s2 = document.getElementById("apriso-flow-svg-2");
if (c1 && s1 && cachedDataFlowA.length) drawFlowchart(c1, s1, "arrow-1", cachedDataFlowA, 1);
if (c2 && s2 && cachedDataFlowB.length) drawFlowchart(c2, s2, "arrow-2", cachedDataFlowB, 2);
function waitForRender(dataFlowA, dataFlowB) {
const c1 = document.getElementById("apriso-flow-canvas-1");
const c2 = document.getElementById("apriso-flow-canvas-2");
const s1 = document.getElementById("apriso-flow-svg-1");
const s2 = document.getElementById("apriso-flow-svg-2");
if (!c1 || !c2 || !s1 || !s2) {
setTimeout(function() { waitForRender(dataFlowA, dataFlowB); }, 500);
if (c1.clientWidth === 0 || c2.clientWidth === 0) {
setTimeout(function() { waitForRender(dataFlowA, dataFlowB); }, 500);
drawFlowchart(c1, s1, "arrow-1", dataFlowA, 1);
drawFlowchart(c2, s2, "arrow-2", dataFlowB, 2);
function drawFlowchart(canvas, svg, markerId, data, side) {
if (!data || !data.length) return;
canvas.querySelectorAll(".node").forEach(x => x.remove());
svg.querySelectorAll("path").forEach(function (p) {
if (!p.parentNode.closest("defs")) p.remove();
const canvasWidth = canvas.clientWidth;
svg.setAttribute("width", canvasWidth);
svg.setAttribute("height", Math.max(canvas.scrollHeight, 1000));
data.forEach(function (x) {
graph[x.opr] = x.destopr === "LAST" ? [] : (Array.isArray(x.destopr) ? x.destopr : [x.destopr]);
data.forEach(function (x) {
if (x.destopr === "Last") return;
(Array.isArray(x.destopr) ? x.destopr : [x.destopr]).forEach(function (d) {
if (indegree[d] == null) indegree[d] = 0;
Object.keys(indegree).forEach(function (k) {
(graph[n] || []).forEach(function (c) {
level[c] = Math.max(level[c] || 0, level[n] + 1);
if (indegree[c] === 0) q.push(c);
Object.keys(level).forEach(function (n) {
if (!groups[level[n]]) groups[level[n]] = [];
groups[level[n]].push(n);
const hGap = Math.min(180, canvasWidth / 2);
const center = (canvasWidth / 2) - (nodeWidth / 2);
Object.keys(groups).forEach(function (l) {
const total = (arr.length - 1) * hGap;
const start = center - total / 2;
arr.forEach(function (n, i) {
function makeNode(id, oprId, x, y, cls, isClickable = true) {
const div = document.createElement("div");
div.className = "node " + (cls || "");
div.id = "node_" + side + "_" + oprId;
div.setAttribute("data-oprid", oprId);
div.onclick = function (e) {
openOperationSidePanel(id, oprId, side);
makeNode("FIRST", "FIRST", center, 10, "start", false);
Object.keys(pos).forEach(function (n) {
const row = data.find(function (x) { return x.opr === n; });
makeNode(n, row ? row.oprId : n, pos[n].x, pos[n].y, "", true);
const maxLevel = Object.keys(level).length ? Math.max.apply(null, Object.values(level)) : 0;
makeNode("LAST", "LAST", center, 80 + (maxLevel + 1) * vGap, "end", false);
function line(x1, y1, x2, y2) {
if (Math.abs(y2 - y1) > 200) {
d = M ${x1} ${y1} L ${x1} ${yy} L ${x2} ${yy} L ${x2} ${y2};
const mid = (y1 + y2) / 2;
d = M ${x1} ${y1} L ${x1} ${mid} L ${x2} ${mid} L ${x2} ${y2};
path.setAttribute("d", d);
path.setAttribute("stroke", "#555");
path.setAttribute("stroke-width", "2");
path.setAttribute("fill", "none");
path.setAttribute("marker-end", "url(#" + markerId + ")");
data.forEach(function (x) {
if (x.source === "FIRST") {
line(center + 45, 52, pos[x.opr].x + 45, pos[x.opr].y);
if (x.destopr === "LAST") {
line(pos[x.opr].x + 45, pos[x.opr].y + 42, center + 45, 80 + (maxLevel + 1) * vGap);
(Array.isArray(x.destopr) ? x.destopr : [x.destopr]).forEach(function (d) {
line(pos[x.opr].x + 45, pos[x.opr].y + 42, pos[d].x + 45, pos[d].y);
/* =========================================================
5. SIDE DETAILS PANEL HANDLERS
========================================================= */
function openOperationSidePanel(nodeText, oprId, side) {
const panel = document.getElementById("panel-details-" + side);
setTimeout(redrawAllFlowcharts, 50);
panel.querySelector(".panelTitle").innerHTML = "Operation : " + nodeText;
call_PCV20_GetProcessOperationDetail(currentcontext, oprId, side);
function closeSidePanel(side){
const panel = document.getElementById("panel-details-" + side);
setTimeout(redrawAllFlowcharts, 50);
function toggleStepTree(btn, stepId, side) {
const container = document.getElementById("step_details_" + side + "_" + stepId);
if (btn.innerText === "+") {
if (!container.getAttribute("data-loaded")) {
call_PCV20_GetProcessOperationStepDetail(currentcontext, stepId, side, container);
/* =========================================================
========================================================= */
function generateCTQHTML(ctqData) {
// Graceful fallback to '-' if data or specific value is missing.
var usl = ctqData && ctqData.USL !== undefined ? ctqData.USL : "-";
var ull = ctqData && ctqData.ULL !== undefined ? ctqData.ULL : "-";
var lcl = ctqData && ctqData.LCL !== undefined ? ctqData.LCL : "-";
var lsl = ctqData && ctqData.LSL !== undefined ? ctqData.LSL : "-";
<div class="ctq-item" title="Upper Specification Limit"><span>USL</span><strong>${usl}</strong></div>
<div class="ctq-item" title="Upper Control Limit"><span>ULL</span><strong>${ull}</strong></div>
<div class="ctq-item" title="Target"><span>Target</span><strong>${target}</strong></div>
<div class="ctq-item" title="Lower Control Limit"><span>LCL</span><strong>${lcl}</strong></div>
<div class="ctq-item" title="Lower Specification Limit"><span>LSL</span><strong>${lsl}</strong></div>
/* =========================================================
========================================================= */
function call_PCV20_GetProcessOperationDetail(context, ProcessOprID, side, successCallback, errorCallback){
inputs.ProcessOprID = ProcessOprID;
context.callDFC('PCV20_GetProcessOperationDetail', inputs,
if (typeof successCallback == "function") {
successCallback(outputs);
const panel = document.getElementById("panel-details-" + side);
// --- NEW: OPERATION CTQ INJECTION ---
var opCTQ = outputs["OperationCTQ"]; // Adjust this to match your actual backend parameter name when you get it
panel.querySelector(".ctqclass").classList.remove("hiddenclass");
panel.querySelector(".ctqContainer").innerHTML = generateCTQHTML(opCTQ);
// ------------------------------------
var stepid = outputs["StepID"];
var stepdet = outputs["StepName"];
if (stepid && stepid.length > 0) {
panel.querySelector(".stepsclass").classList.remove("hiddenclass");
for (var i = 0; i < stepid.length; i++) {
var currentStepId = stepid[i];
var currentStepName = stepdet[i];
// Added StepCTQ placeholder li inside the tree
<div class="step-header" onclick="toggleStepTree(this.querySelector('.tree-toggle-btn'), '${currentStepId}', ${side})">
<span class="tree-toggle-btn">+</span>
<span class="step-item">${currentStepName}</span>
<div id="step_details_${side}_${currentStepId}" class="step-child-container" style="display:none;">
<ul class="tree step-child-tree">
<li class="Stepctqclass hiddenclass"><span class="folder">🎯 Step CTQ</span><div class="StepctqContainer"></div></li>
<li class="Stepcharacterclass hiddenclass"><span class="folder">📐 Characteristics</span><ul class="StepcharactersList"></ul></li>
<li class="Stepcomponentclass hiddenclass"><span class="folder">🧩 Components</span><ul class="StepcomponentsList"></ul></li>
<li class="Stepdocumentclass hiddenclass"><span class="folder">📄 Documents</span><ul class="StepdocumentsList"></ul></li>
<li class="Stepworkclass hiddenclass"><span class="folder">🏭 Work Centers</span><ul class="StepworksList"></ul></li>
<li class="Stepresourceclass hiddenclass"><span class="folder">🔧 Resources</span><ul class="StepresourcesList"></ul></li>
panel.querySelector(".stepList").innerHTML = stepIdHTML;
panel.querySelector(".stepList").innerHTML = "";
panel.querySelector(".stepsclass").classList.add("hiddenclass");
var Characteristics = outputs["Characteristics"];
if (Characteristics && Characteristics.length > 0) {
panel.querySelector(".charclass").classList.remove("hiddenclass");
for (var m = 0; m < Characteristics.length; m++) { charHTML += "<li>" + Characteristics[m] + "</li>"; }
panel.querySelector(".charList").innerHTML = charHTML;
panel.querySelector(".charclass").classList.add("hiddenclass");
var Resource = outputs["ResuourceName"];
if (Resource && Resource.length > 0) {
panel.querySelector(".resclass").classList.remove("hiddenclass");
for (var j = 0; j < Resource.length; j++) { ResHTML += "<li>" + Resource[j] + "</li>"; }
panel.querySelector(".resourceList").innerHTML = ResHTML;
panel.querySelector(".resclass").classList.add("hiddenclass");
var Component = outputs["ProductNo"];
if (Component && Component.length > 0) {
panel.querySelector(".compclass").classList.remove("hiddenclass");
for (var k = 0; k < Component.length; k++) { CompHTML += "<li>" + Component[k] + "</li>"; }
panel.querySelector(".componentList").innerHTML = CompHTML;
panel.querySelector(".compclass").classList.add("hiddenclass");
var Document = outputs["Name"];
if (Document && Document.length > 0) {
panel.querySelector(".docclass").classList.remove("hiddenclass");
for (var l = 0; l < Document.length; l++) { DocHTML += "<li>" + Document[l] + "</li>"; }
panel.querySelector(".docList") ? panel.querySelector(".docList").innerHTML = DocHTML : panel.querySelector(".documentList").innerHTML = DocHTML;
panel.querySelector(".docclass").classList.add("hiddenclass");
var WorkCenter = outputs["WorkCenter"];
panel.querySelector(".wcclass").classList.remove("hiddenclass");
var WCHTML = "<li>" + WorkCenter + "</li>";
panel.querySelector(".workcenterList").innerHTML = WCHTML;
panel.querySelector(".wcclass").classList.add("hiddenclass");
function call_PCV20_GetProcessOperationStepDetail(context, ProcessOprStepID, side, stepContainer, errorCallback){
inputs.ProcessOprStepID = ProcessOprStepID;
context.callDFC('PCV20_GetProcessOperationStepDetail', inputs,
stepContainer.setAttribute("data-loaded", "true");
// --- NEW: STEP CTQ INJECTION ---
var stepCTQ = outputs["StepCTQ"]; // Adjust this to match your actual backend parameter name when you get it
stepContainer.querySelector(".Stepctqclass").classList.remove("hiddenclass");
stepContainer.querySelector(".StepctqContainer").innerHTML = generateCTQHTML(stepCTQ);
// -------------------------------
var Characteristics = outputs["Characteristics"];
if (Characteristics && Characteristics.length > 0) {
stepContainer.querySelector(".Stepcharacterclass").classList.remove("hiddenclass");
for (var m = 0; m < Characteristics.length; m++) {
charHTML += "<li>" + Characteristics[m] + "</li>";
stepContainer.querySelector(".StepcharactersList").innerHTML = charHTML;
var Resource = outputs["ResuourceName"] || outputs["ResourceName"];
if (Resource && Resource.length > 0) {
stepContainer.querySelector(".Stepresourceclass").classList.remove("hiddenclass");
for (var j = 0; j < Resource.length; j++) {
ResHTML += "<li>" + Resource[j] + "</li>";
stepContainer.querySelector(".StepresourcesList").innerHTML = ResHTML;
var Component = outputs["ProductNo"];
if (Component && Component.length > 0) {
stepContainer.querySelector(".Stepcomponentclass").classList.remove("hiddenclass");
for (var k = 0; k < Component.length; k++) {
CompHTML += "<li>" + Component[k] + "</li>";
stepContainer.querySelector(".StepcomponentsList").innerHTML = CompHTML;
var Document = outputs["Name"];
if (Document && Document.length > 0) {
stepContainer.querySelector(".Stepdocumentclass").classList.remove("hiddenclass");
for (var l = 0; l < Document.length; l++) {
DocHTML += "<li>" + Document[l] + "</li>";
stepContainer.querySelector(".StepdocumentsList").innerHTML = DocHTML;
var WorkCenter = outputs["WorkCenter"];
stepContainer.querySelector(".Stepworkclass").classList.remove("hiddenclass");
var WCHTML = "<li>" + WorkCenter + "</li>";
stepContainer.querySelector(".StepworksList").innerHTML = WCHTML;
function call_PCV20_AuthorizeProcess(context, ProcessId, Effectivedate, successCallback, errorCallback){
inputs.ProcessId = ProcessId;
inputs.Effectivedate = Effectivedate;
context.callDFC('PCV20.AuthorizeProcess', inputs,
if (typeof successCallback == "function")
successCallback(outputs);
context.outputs.Action = "REFRESH";
}
/* =========================================================
1. OUTER LAYOUT CONTAINER
========================================================= */
/* =========================================================
2. FLOWCHART OUTER PANELS & THEMES
========================================================= */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
position: relative; /* Anchor point for fixed bottom-right authorize button */
font-family: Arial, sans-serif;
/* Side 1 (Dark Blue Theme) */
.panel-side-1 { border-top: 3px solid #0F4C81; }
.panel-title-1 { background: #0F4C81; color: #ffffff; border-bottom: 1px solid #0B3C66; }
.side-panel-1 { border: 1px solid #0F4C81; border-top: 3px solid #0F4C81; }
.header-1 { background: #0F4C81 !important; color: #ffffff; }
/* Side 2 (Cyan / Light Blue Theme) */
.panel-side-2 { border-top: 3px solid #2B88D8; }
.panel-title-2 { background: #2B88D8; color: #ffffff; border-bottom: 1px solid #206FB3; }
.side-panel-2 { border: 1px solid #2B88D8; border-top: 3px solid #2B88D8; }
.header-2 { background: #2B88D8 !important; color: #ffffff; }
/* =========================================================
========================================================= */
.middle-panel-container {
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
.popup-header { padding: 10px; font-weight: bold; display: flex; justify-content: space-between; font-size: 13px; }
.popup-body { padding: 10px; overflow-y: auto; flex: 1; font-size: 12px; }
.close { cursor: pointer; font-size: 14px; }
.close:hover { color: #ffcccc; }
/* =========================================================
4. CANVAS & FLOWCHART NODES
========================================================= */
.flow-canvas { position: relative; width: 100%; height: 100%; overflow-y: auto; overflow-x: hidden; background: #f9f9f9; font-family: Arial, sans-serif; }
.flow-canvas .node { position: absolute; width: 90px; height: 42px; background: #4CAF50; color: #fff; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-weight: bold; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); z-index: 10; cursor: pointer; font-size: 12px; box-sizing: border-box; }
.flow-canvas .start, .flow-canvas .end { border-radius: 22px; cursor: default !important; font-size: 11px; letter-spacing: 0.5px; text-transform: uppercase; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12); }
.flow-canvas .start { background: linear-gradient(135deg, #1e88e5, #1565c0); border: 1px solid #0d47a1; }
.flow-canvas .end { background: linear-gradient(135deg, #e53935, #c62828); border: 1px solid #b71c1c; }
/* =========================================================
5. FLOATING AUTHORIZE BUTTON
========================================================= */
.fixed-authorize-container { position: absolute; bottom: 20px; right: 20px; z-index: 500; display: flex; flex-direction: column; align-items: center; background: rgba(255, 255, 255, 0.95); padding: 6px 10px; border-radius: 6px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25); border: 1px solid #cbd5e1; }
.authorize-icon { font-size: 22px; color: #374151; margin-bottom: 3px; }
.authorize-btn { background: #4b5563; color: #ffffff; border: none; padding: 5px 12px; font-size: 11px; font-weight: bold; border-radius: 4px; cursor: pointer; transition: background 0.2s; }
.authorize-btn:hover { background: #1f2937; }
/* =========================================================
6. AUTHORIZATION MODAL & CUSTOM CALENDAR WIDGET
========================================================= */
.auth-modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.45); z-index: 2000; display: flex; align-items: center; justify-content: center; }
.auth-modal-content { background: #ffffff; width: 380px; border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); overflow: visible; display: flex; flex-direction: column; font-family: Arial, sans-serif; }
.auth-modal-header { background: #6b7280; color: #ffffff; padding: 12px 16px; font-size: 13.5px; font-weight: bold; display: flex; justify-content: space-between; align-items: center; border-top-left-radius: 6px; border-top-right-radius: 6px; }
.auth-modal-body { padding: 20px 16px; }
.auth-form-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.auth-label { font-size: 12.5px; font-weight: 600; color: #374151; white-space: nowrap; }
.custom-datepicker-container, .custom-picker-wrapper { position: relative; display: inline-block; }
.apr-input, input.effectivedate { font-size: 12px !important; padding: 6px 10px !important; height: 32px !important; border: 1px solid #94a3b8 !important; border-radius: 4px !important; outline: none; cursor: pointer; background-color: #ffffff; box-sizing: border-box; }
.day.disabled-day { color: #cbd5e1 !important; cursor: not-allowed !important; background-color: transparent !important; opacity: 0.5; }
.day.disabled-day:hover { background: transparent !important; }
.day.disabled-day, .wheel-item.disabled-time { color: #888888 !important; background-color: #f1f5f9 !important; cursor: not-allowed !important; opacity: 1 !important; pointer-events: none; font-weight: 400; }
.day.disabled-day:hover, .wheel-item.disabled-time:hover { background-color: #f1f5f9 !important; color: #888888 !important; }
.custom-picker-dropdown { position: absolute; top: 36px; right: 0; width: 320px; background: #ffffff; border: 1px solid #cbd5e1; border-radius: 6px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); z-index: 2500; padding: 12px; font-size: 12px; font-family: Arial, sans-serif; }
.picker-header, .picker-top-bar { display: flex; justify-content: space-between; align-items: center; font-weight: bold; font-size: 13px; margin-bottom: 8px; }
.picker-header button, .month-nav-btns button { background: none; border: none; cursor: pointer; font-weight: bold; font-size: 14px; color: #4b5563; padding: 0 6px; }
.picker-header button:hover, .month-nav-btns button:hover { color: #111827; }
.picker-body, .picker-main-body { display: flex; gap: 8px; }
.calendar-section { flex: 1; }
.weekdays, .days-grid { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; }
.weekdays { font-weight: bold; color: #64748b; margin-bottom: 4px; }
.weekdays span { font-size: 11px; font-weight: bold; color: #333; padding-bottom: 4px; }
.day { padding: 5px 0; cursor: pointer; border-radius: 2px; font-size: 12px; color: #1e293b; font-weight: 500; }
.day:hover:not(.empty):not(.disabled-day):not(.selected) { background: #e2e8f0; }
.day.selected { background: #475569 !important; color: #ffffff !important; font-weight: bold; border-radius: 2px; }
.day.today-highlight { border: 2px solid #1e293b; }
.wheel-item { font-size: 13px; padding: 6px 10px; cursor: pointer; color: #1e293b; border-radius: 4px; margin: 2px 0; text-align: center; transition: background 0.15s ease; }
.time-section, .time-wheels-container { display: flex; gap: 4px; border-left: 1px solid #e2e8f0; padding-left: 8px; justify-content: center; }
.time-col { display: flex; flex-direction: column; align-items: center; }
.time-col-header { background: #cccccc; color: #000000; font-weight: bold; font-size: 13px; padding: 8px 14px; border-radius: 2px; margin-bottom: 8px; min-width: 28px; text-align: center; }
.time-col label { font-size: 10px; font-weight: bold; color: #64748b; }
.time-col select { font-size: 11px; padding: 2px; border-radius: 3px; border: 1px solid #cbd5e1; }
.time-wheel { width: 36px; height: 150px; overflow-y: auto; scrollbar-width: none; display: flex; flex-direction: column; align-items: center; }
.time-wheel::-webkit-scrollbar { display: none; }
.wheel-item:hover:not(.disabled-time):not(.selected) { background: #e2e8f0; }
.wheel-item.selected { background: #3b82f6 !important; color: #ffffff !important; font-weight: bold; }
.picker-actions, .picker-bottom-bar { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; padding-top: 8px; border-top: 1px solid #e2e8f0; }
.quick-links { display: flex; gap: 12px; }
.quick-links span { color: #0066cc; font-size: 11px; cursor: pointer; }
.quick-links span:hover { text-decoration: underline; }
.picker-action-btns { display: flex; gap: 6px; }
.picker-btn, .btn-picker-cancel, .btn-picker-ok { padding: 4px 12px; font-size: 11px; font-weight: bold; border-radius: 4px; cursor: pointer; border: none; }
.picker-cancel-btn, .btn-picker-cancel { background: #ffffff; color: #374151; border: 1px solid #d1d5db; }
.picker-cancel-btn:hover, .btn-picker-cancel:hover { background: #f3f4f6; }
.picker-ok-btn, .btn-picker-ok { background: #2563eb; color: #ffffff; border: 1px solid #1d4ed8; }
.picker-ok-btn:hover, .btn-picker-ok:hover { background: #1d4ed8; }
.auth-modal-footer { padding: 10px 16px; background: #f8fafc; border-top: 1px solid #e2e8f0; display: flex; justify-content: flex-end; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; }
.auth-save-btn { background: #6b7280; color: #ffffff; border: none; padding: 6px 14px; font-size: 12px; font-weight: bold; border-radius: 4px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: background 0.2s; }
.auth-save-btn:hover:not(:disabled) { background: #4b5563; }
.auth-save-btn:disabled { background: #9ca3af; cursor: not-allowed; opacity: 0.6; }
/* =========================================================
7. TREE LIST & STEP EXPANDERS
========================================================= */
.tree, .tree ul { padding-left: 0; list-style-type: none; margin: 0; }
.tree > li { margin-bottom: 12px; }
.folder { font-weight: bold; display: inline-block; margin-bottom: 6px; color: #333; font-size: 12px; }
.stepList { display: flex; flex-direction: column; gap: 6px; padding-left: 2px; }
.step-node { background: #fbfbfb; border: 1px solid #e2e8f0; border-radius: 4px; padding: 6px 8px; transition: background 0.2s, border-color 0.2s; }
.step-node:hover { background: #f1f5f9; border-color: #cbd5e1; }
.step-header { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
.tree-toggle-btn { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; min-width: 14px; line-height: 12px; border: 1px solid #64748b; background: #ffffff; color: #334155; font-size: 11px; font-family: monospace; font-weight: bold; cursor: pointer; border-radius: 2px; user-select: none; margin-top: 2px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); }
.tree-toggle-btn:hover { background: #0F4C81; color: #ffffff; border-color: #0F4C81; }
.step-item { font-weight: 600; color: #1e293b; font-size: 11.5px; line-height: 1.4; word-break: break-word; }
.step-child-tree { margin-top: 6px !important; margin-left: 6px !important; padding-left: 10px !important; border-left: 2px solid #cbd5e1; }
.side-panel-1 .step-child-tree { border-left-color: #0F4C81; }
.side-panel-2 .step-child-tree { border-left-color: #2B88D8; }
.step-child-tree li { margin: 4px 0; font-size: 11px; color: #475569; }
.step-child-tree ul { padding-left: 12px; }
.hiddenclass { display: none !important; }
/* =========================================================
========================================================= */
.ctqContainer, .StepctqContainer {
border: 1px solid #cbd5e1;
min-width: 35px; /* Ensures they don't crush too small */
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
text-transform: uppercase;