ui: split wizard state and shell logic
This commit is contained in:
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
## Current
|
## Current
|
||||||
|
|
||||||
### `feat/frontend-wizard-modularization`
|
### `feat/frontend-build-pipeline`
|
||||||
|
|
||||||
Status: in_progress
|
Status: in_progress
|
||||||
|
|
||||||
DoD:
|
DoD:
|
||||||
- `wizard.js` is split into smaller modules by responsibility
|
- UI assets are bundled through a lightweight pipeline suitable for the current stack
|
||||||
- global mutable wizard state is reduced to one explicit shared state object
|
- local source files remain readable, but shipped JS is no longer served as dozens of unbundled scripts
|
||||||
- protocol-specific logic is separated from navigation/rendering plumbing
|
- vendor dependencies move under package-managed installation where practical
|
||||||
- the existing wizard flow keeps working after the split
|
- Docker UI image builds the frontend assets before packaging them into nginx
|
||||||
- targeted wizard e2e coverage stays green
|
- existing UI e2e coverage stays green after the build change
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
- `feat/frontend-build-pipeline`
|
- `feat/frontend-css-state-cleanup`
|
||||||
|
|
||||||
## Backlog
|
## Backlog
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
<link rel="stylesheet" href="../../css/pages.css">
|
<link rel="stylesheet" href="../../css/pages.css">
|
||||||
<script src="../../js/config.js"></script>
|
<script src="../../js/config.js"></script>
|
||||||
<script src="../../js/i18n.js"></script>
|
<script src="../../js/i18n.js"></script>
|
||||||
|
<script src="../../js/dom.js"></script>
|
||||||
|
<script src="../../js/wizard-state.js"></script>
|
||||||
|
<script src="../../js/wizard-shell.js"></script>
|
||||||
<script src="../../js/workspace.js"></script>
|
<script src="../../js/workspace.js"></script>
|
||||||
<script src="../../js/api.js"></script>
|
<script src="../../js/api.js"></script>
|
||||||
<script src="../../js/ui-feedback.js"></script>
|
<script src="../../js/ui-feedback.js"></script>
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
(function() {
|
||||||
|
var TOTAL_STEPS = 5;
|
||||||
|
var CHECK_SVG = '<svg width="10" height="10"><use href="' + (window.APP_BASE || '') + 'icons/general/check.svg#icon"/></svg>';
|
||||||
|
var ARROW_SVG = '<svg width="13" height="13"><use href="' + (window.APP_BASE || '') + 'icons/general/arrow-right.svg#icon"/></svg>';
|
||||||
|
|
||||||
|
function tKey(key) {
|
||||||
|
return typeof t === 'function' ? t(key) : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tfKey(key, vars) {
|
||||||
|
return typeof tf === 'function' ? tf(key, vars) : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function step3PanelId() {
|
||||||
|
if (window.wizardProtocol === 'graphql') return 'step-panel-3-graphql';
|
||||||
|
if (window.wizardProtocol === 'grpc') return 'step-panel-3-grpc';
|
||||||
|
if (window.wizardProtocol === 'websocket') return 'step-panel-3-websocket';
|
||||||
|
if (window.wizardProtocol === 'soap') return 'step-panel-3-soap';
|
||||||
|
return 'step-panel-3-rest';
|
||||||
|
}
|
||||||
|
|
||||||
|
function stepFile(step) {
|
||||||
|
return step === 3 ? 'step3-' + window.wizardProtocol + '.html' : 'step' + step + '.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadStep(step, callback) {
|
||||||
|
var panelId = step === 3 ? step3PanelId() : 'step-panel-' + step;
|
||||||
|
if (document.getElementById(panelId)) {
|
||||||
|
if (callback) callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch(stepFile(step))
|
||||||
|
.then(function(response) { return response.text(); })
|
||||||
|
.then(function(html) {
|
||||||
|
var container = document.getElementById('step-panel-container');
|
||||||
|
if (container) container.insertAdjacentHTML('beforeend', html);
|
||||||
|
if (typeof applyLang === 'function') applyLang();
|
||||||
|
if (callback) callback();
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
if (callback) callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function doGoToStep(step) {
|
||||||
|
document.querySelectorAll('.step-number[data-step]').forEach(function(element) {
|
||||||
|
var stepNumber = Number(element.getAttribute('data-step')) || 0;
|
||||||
|
element.textContent = tfKey('wizard.step_short', { step: stepNumber });
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.step-pane').forEach(function(pane) { pane.style.display = 'none'; });
|
||||||
|
var panelId = step === 3 ? step3PanelId() : 'step-panel-' + step;
|
||||||
|
var pane = document.getElementById(panelId);
|
||||||
|
if (pane) pane.style.display = '';
|
||||||
|
|
||||||
|
document.querySelectorAll('.step-item').forEach(function(item, index) {
|
||||||
|
var stepNumber = index + 1;
|
||||||
|
item.classList.remove('active', 'done', 'pending');
|
||||||
|
var indicator = item.querySelector('.step-indicator');
|
||||||
|
var status = item.querySelector('.step-status-text');
|
||||||
|
if (stepNumber < step) {
|
||||||
|
item.classList.add('done');
|
||||||
|
if (indicator) indicator.innerHTML = CHECK_SVG;
|
||||||
|
if (status) status.textContent = tKey('wizard.status.completed');
|
||||||
|
} else if (stepNumber === step) {
|
||||||
|
item.classList.add('active');
|
||||||
|
if (indicator) indicator.textContent = stepNumber;
|
||||||
|
if (status) status.textContent = tKey('wizard.status.in_progress');
|
||||||
|
} else {
|
||||||
|
item.classList.add('pending');
|
||||||
|
if (indicator) indicator.textContent = stepNumber;
|
||||||
|
if (status) status.textContent = tKey('wizard.status.not_started');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var percent = Math.round((step / TOTAL_STEPS) * 100);
|
||||||
|
var fill = document.querySelector('.progress-bar-fill');
|
||||||
|
if (fill) fill.style.width = percent + '%';
|
||||||
|
var percentNode = document.querySelector('.progress-pct');
|
||||||
|
if (percentNode) percentNode.textContent = percent + '%';
|
||||||
|
|
||||||
|
var counter = document.querySelector('[data-step-counter]');
|
||||||
|
if (counter) counter.innerHTML = tfKey('wizard.step_of', { step: step, total: TOTAL_STEPS });
|
||||||
|
|
||||||
|
var backButton = document.querySelector('.btn-back');
|
||||||
|
if (backButton) {
|
||||||
|
if (step === 1) {
|
||||||
|
backButton.disabled = true;
|
||||||
|
backButton.style.opacity = '0.35';
|
||||||
|
backButton.style.cursor = 'not-allowed';
|
||||||
|
} else {
|
||||||
|
backButton.disabled = false;
|
||||||
|
backButton.style.opacity = '';
|
||||||
|
backButton.style.cursor = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var continueButton = document.querySelector('.btn-continue');
|
||||||
|
if (continueButton) {
|
||||||
|
if (step === TOTAL_STEPS) {
|
||||||
|
var finalLabel = window.wizardMode === 'edit' ? tKey('wizard.button.save_changes') : tKey('wizard.button.create');
|
||||||
|
continueButton.innerHTML = finalLabel + ' ' + ARROW_SVG;
|
||||||
|
continueButton.style.cssText = 'background:#3fb950;border-color:#2ea043;box-shadow:0 1px 4px rgba(63,185,80,0.4);';
|
||||||
|
} else {
|
||||||
|
continueButton.innerHTML = tKey('wizard.button.continue') + ' ' + ARROW_SVG;
|
||||||
|
continueButton.style.cssText = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.currentStep = step;
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToStep(step) {
|
||||||
|
if (step < 1 || step > TOTAL_STEPS) return;
|
||||||
|
loadStep(step, function() { doGoToStep(step); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWizardPanels(steps) {
|
||||||
|
return steps.reduce(function(chain, step) {
|
||||||
|
return chain.then(function() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
loadStep(step, resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, Promise.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindProtocolCards() {
|
||||||
|
document.querySelectorAll('.protocol-card').forEach(function(card) {
|
||||||
|
card.addEventListener('click', function() {
|
||||||
|
document.querySelectorAll('.protocol-card').forEach(function(item) {
|
||||||
|
item.classList.remove('selected');
|
||||||
|
item.setAttribute('aria-checked', 'false');
|
||||||
|
});
|
||||||
|
card.classList.add('selected');
|
||||||
|
card.setAttribute('aria-checked', 'true');
|
||||||
|
|
||||||
|
var protocol = card.dataset.protocol
|
||||||
|
|| (card.classList.contains('rest')
|
||||||
|
? 'rest'
|
||||||
|
: card.classList.contains('graphql')
|
||||||
|
? 'graphql'
|
||||||
|
: card.classList.contains('grpc')
|
||||||
|
? 'grpc'
|
||||||
|
: card.classList.contains('websocket')
|
||||||
|
? 'websocket'
|
||||||
|
: card.classList.contains('soap')
|
||||||
|
? 'soap'
|
||||||
|
: 'rest');
|
||||||
|
|
||||||
|
window.wizardProtocol = protocol;
|
||||||
|
var step3Name = document.getElementById('sidebar-step-3-name');
|
||||||
|
var labels = window.CrankWizardState.step3Labels();
|
||||||
|
if (step3Name) step3Name.textContent = labels[protocol] || tKey('wizard.step3.rest.label');
|
||||||
|
|
||||||
|
if (protocol === 'graphql') {
|
||||||
|
var pathInput = document.getElementById('endpoint-path');
|
||||||
|
if (pathInput && pathInput.value === '/v1/leads') pathInput.value = '/graphql';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
card.addEventListener('keydown', function(event) {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
card.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.CrankWizardShell = {
|
||||||
|
TOTAL_STEPS: TOTAL_STEPS,
|
||||||
|
step3PanelId: step3PanelId,
|
||||||
|
loadStep: loadStep,
|
||||||
|
doGoToStep: doGoToStep,
|
||||||
|
goToStep: goToStep,
|
||||||
|
loadWizardPanels: loadWizardPanels,
|
||||||
|
bindProtocolCards: bindProtocolCards,
|
||||||
|
};
|
||||||
|
}());
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
(function() {
|
||||||
|
function tKey(key) {
|
||||||
|
return typeof t === 'function' ? t(key) : key;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
currentStep: 1,
|
||||||
|
wizardProtocol: 'rest',
|
||||||
|
wizardMode: 'create',
|
||||||
|
wizardEditId: null,
|
||||||
|
wizardWorkspaceId: null,
|
||||||
|
wizardCurrentOperation: null,
|
||||||
|
wizardCurrentVersion: null,
|
||||||
|
wizardProtoUpload: null,
|
||||||
|
wizardDescriptorSetUpload: null,
|
||||||
|
wizardSoapWsdlUpload: null,
|
||||||
|
wizardSoapXsdUpload: null,
|
||||||
|
wizardTestResponsePreview: null,
|
||||||
|
grpcDescriptorServices: [],
|
||||||
|
soapServiceCatalog: [],
|
||||||
|
wizardProtocolCapabilities: null,
|
||||||
|
wizardSecrets: [],
|
||||||
|
wizardAuthProfiles: [],
|
||||||
|
selectedUpstreamId: null,
|
||||||
|
editingUpstreamId: null,
|
||||||
|
protoParsed: null,
|
||||||
|
selectedRpcMethod: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
function defaultProtocolCapabilities() {
|
||||||
|
return {
|
||||||
|
rest: {
|
||||||
|
supports_execution_modes: ['unary', 'window', 'session', 'async_job'],
|
||||||
|
supports_transport_behaviors: ['request_response', 'server_stream', 'deferred_result'],
|
||||||
|
},
|
||||||
|
graphql: {
|
||||||
|
supports_execution_modes: ['unary'],
|
||||||
|
supports_transport_behaviors: ['request_response'],
|
||||||
|
},
|
||||||
|
grpc: {
|
||||||
|
supports_execution_modes: ['unary', 'window', 'session', 'async_job'],
|
||||||
|
supports_transport_behaviors: ['request_response', 'server_stream'],
|
||||||
|
},
|
||||||
|
websocket: {
|
||||||
|
supports_execution_modes: ['window', 'session', 'async_job'],
|
||||||
|
supports_transport_behaviors: ['server_stream', 'stateful_session', 'deferred_result'],
|
||||||
|
},
|
||||||
|
soap: {
|
||||||
|
supports_execution_modes: ['unary', 'async_job'],
|
||||||
|
supports_transport_behaviors: ['request_response', 'server_stream', 'deferred_result'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentProtocolCapabilities() {
|
||||||
|
var capabilities = state.wizardProtocolCapabilities || defaultProtocolCapabilities();
|
||||||
|
return capabilities[state.wizardProtocol] || capabilities.rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProtocolCapabilities() {
|
||||||
|
if (!state.wizardWorkspaceId || !window.CrankApi || typeof window.CrankApi.getProtocolCapabilities !== 'function') {
|
||||||
|
state.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||||
|
return state.wizardProtocolCapabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await window.CrankApi.getProtocolCapabilities(state.wizardWorkspaceId);
|
||||||
|
var mapped = {};
|
||||||
|
(response.items || []).forEach(function(item) {
|
||||||
|
mapped[item.protocol] = item;
|
||||||
|
});
|
||||||
|
state.wizardProtocolCapabilities = mapped;
|
||||||
|
} catch (_error) {
|
||||||
|
state.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.wizardProtocolCapabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
function step3Labels() {
|
||||||
|
return {
|
||||||
|
rest: tKey('wizard.step3.rest.label'),
|
||||||
|
graphql: tKey('wizard.step3.graphql.label'),
|
||||||
|
grpc: tKey('wizard.step3.grpc.label'),
|
||||||
|
websocket: tKey('wizard.step3.websocket.label'),
|
||||||
|
soap: tKey('wizard.step3.soap.label'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.CrankWizardState = {
|
||||||
|
state: state,
|
||||||
|
defaultProtocolCapabilities: defaultProtocolCapabilities,
|
||||||
|
currentProtocolCapabilities: currentProtocolCapabilities,
|
||||||
|
loadProtocolCapabilities: loadProtocolCapabilities,
|
||||||
|
step3Labels: step3Labels,
|
||||||
|
};
|
||||||
|
|
||||||
|
[
|
||||||
|
'currentStep',
|
||||||
|
'wizardProtocol',
|
||||||
|
'wizardMode',
|
||||||
|
'wizardEditId',
|
||||||
|
'wizardWorkspaceId',
|
||||||
|
'wizardCurrentOperation',
|
||||||
|
'wizardCurrentVersion',
|
||||||
|
'wizardProtoUpload',
|
||||||
|
'wizardDescriptorSetUpload',
|
||||||
|
'wizardSoapWsdlUpload',
|
||||||
|
'wizardSoapXsdUpload',
|
||||||
|
'wizardTestResponsePreview',
|
||||||
|
'grpcDescriptorServices',
|
||||||
|
'soapServiceCatalog',
|
||||||
|
'wizardProtocolCapabilities',
|
||||||
|
'wizardSecrets',
|
||||||
|
'wizardAuthProfiles',
|
||||||
|
'selectedUpstreamId',
|
||||||
|
'editingUpstreamId',
|
||||||
|
'protoParsed',
|
||||||
|
'selectedRpcMethod'
|
||||||
|
].forEach(function(key) {
|
||||||
|
Object.defineProperty(window, key, {
|
||||||
|
configurable: true,
|
||||||
|
get: function() {
|
||||||
|
return state[key];
|
||||||
|
},
|
||||||
|
set: function(value) {
|
||||||
|
state[key] = value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}());
|
||||||
+13
-239
@@ -1,247 +1,18 @@
|
|||||||
var currentStep = 1;
|
|
||||||
var TOTAL_STEPS = 5;
|
|
||||||
var wizardProtocol = 'rest'; // 'rest' | 'graphql' | 'grpc' | 'websocket' | 'soap'
|
|
||||||
var wizardMode = 'create'; // 'create' | 'edit'
|
|
||||||
var wizardEditId = null;
|
|
||||||
var wizardWorkspaceId = null;
|
|
||||||
var wizardCurrentOperation = null;
|
|
||||||
var wizardCurrentVersion = null;
|
|
||||||
var wizardProtoUpload = null;
|
|
||||||
var wizardDescriptorSetUpload = null;
|
|
||||||
var wizardSoapWsdlUpload = null;
|
|
||||||
var wizardSoapXsdUpload = null;
|
|
||||||
var wizardTestResponsePreview = null;
|
|
||||||
var grpcDescriptorServices = [];
|
|
||||||
var soapServiceCatalog = [];
|
|
||||||
var wizardProtocolCapabilities = null;
|
|
||||||
function tKey(key) {
|
function tKey(key) {
|
||||||
return typeof t === 'function' ? t(key) : key;
|
return typeof t === 'function' ? t(key) : key;
|
||||||
}
|
}
|
||||||
function tfKey(key, vars) {
|
function tfKey(key, vars) {
|
||||||
return typeof tf === 'function' ? tf(key, vars) : key;
|
return typeof tf === 'function' ? tf(key, vars) : key;
|
||||||
}
|
}
|
||||||
|
var TOTAL_STEPS = window.CrankWizardShell.TOTAL_STEPS;
|
||||||
function defaultProtocolCapabilities() {
|
var loadProtocolCapabilities = window.CrankWizardState.loadProtocolCapabilities;
|
||||||
return {
|
var currentProtocolCapabilities = window.CrankWizardState.currentProtocolCapabilities;
|
||||||
rest: {
|
var loadStep = window.CrankWizardShell.loadStep;
|
||||||
supports_execution_modes: ['unary', 'window', 'session', 'async_job'],
|
var goToStep = window.CrankWizardShell.goToStep;
|
||||||
supports_transport_behaviors: ['request_response', 'server_stream', 'deferred_result'],
|
var _doGoToStep = window.CrankWizardShell.doGoToStep;
|
||||||
},
|
var loadWizardPanels = window.CrankWizardShell.loadWizardPanels;
|
||||||
graphql: {
|
var bindProtocolCards = window.CrankWizardShell.bindProtocolCards;
|
||||||
supports_execution_modes: ['unary'],
|
var step3PanelId = window.CrankWizardShell.step3PanelId;
|
||||||
supports_transport_behaviors: ['request_response'],
|
|
||||||
},
|
|
||||||
grpc: {
|
|
||||||
supports_execution_modes: ['unary', 'window', 'session', 'async_job'],
|
|
||||||
supports_transport_behaviors: ['request_response', 'server_stream'],
|
|
||||||
},
|
|
||||||
websocket: {
|
|
||||||
supports_execution_modes: ['window', 'session', 'async_job'],
|
|
||||||
supports_transport_behaviors: ['server_stream', 'stateful_session', 'deferred_result'],
|
|
||||||
},
|
|
||||||
soap: {
|
|
||||||
supports_execution_modes: ['unary', 'async_job'],
|
|
||||||
supports_transport_behaviors: ['request_response', 'server_stream', 'deferred_result'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentProtocolCapabilities() {
|
|
||||||
var capabilities = wizardProtocolCapabilities || defaultProtocolCapabilities();
|
|
||||||
return capabilities[wizardProtocol] || capabilities.rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadProtocolCapabilities() {
|
|
||||||
if (!wizardWorkspaceId || !window.CrankApi || typeof window.CrankApi.getProtocolCapabilities !== 'function') {
|
|
||||||
wizardProtocolCapabilities = defaultProtocolCapabilities();
|
|
||||||
return wizardProtocolCapabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var response = await window.CrankApi.getProtocolCapabilities(wizardWorkspaceId);
|
|
||||||
var mapped = {};
|
|
||||||
(response.items || []).forEach(function(item) {
|
|
||||||
mapped[item.protocol] = item;
|
|
||||||
});
|
|
||||||
wizardProtocolCapabilities = mapped;
|
|
||||||
} catch (_error) {
|
|
||||||
wizardProtocolCapabilities = defaultProtocolCapabilities();
|
|
||||||
}
|
|
||||||
|
|
||||||
return wizardProtocolCapabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Dynamic step loading ── */
|
|
||||||
function _stepFile(n) {
|
|
||||||
return (n === 3) ? 'step3-' + wizardProtocol + '.html' : 'step' + n + '.html';
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadStep(n, callback) {
|
|
||||||
var panelId = (n === 3) ? step3PanelId() : 'step-panel-' + n;
|
|
||||||
if (document.getElementById(panelId)) { if (callback) callback(); return; }
|
|
||||||
fetch(_stepFile(n))
|
|
||||||
.then(function(r) { return r.text(); })
|
|
||||||
.then(function(html) {
|
|
||||||
var c = document.getElementById('step-panel-container');
|
|
||||||
if (c) c.insertAdjacentHTML('beforeend', html);
|
|
||||||
if (typeof applyLang === 'function') applyLang();
|
|
||||||
if (callback) callback();
|
|
||||||
})
|
|
||||||
.catch(function() { if (callback) callback(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 3 panel id by protocol */
|
|
||||||
function step3PanelId() {
|
|
||||||
if (wizardProtocol === 'graphql') return 'step-panel-3-graphql';
|
|
||||||
if (wizardProtocol === 'grpc') return 'step-panel-3-grpc';
|
|
||||||
if (wizardProtocol === 'websocket') return 'step-panel-3-websocket';
|
|
||||||
if (wizardProtocol === 'soap') return 'step-panel-3-soap';
|
|
||||||
return 'step-panel-3-rest';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar step-3 label by protocol */
|
|
||||||
var STEP3_LABELS = {
|
|
||||||
rest: tKey('wizard.step3.rest.label'),
|
|
||||||
graphql: tKey('wizard.step3.graphql.label'),
|
|
||||||
grpc: tKey('wizard.step3.grpc.label'),
|
|
||||||
websocket: tKey('wizard.step3.websocket.label'),
|
|
||||||
soap: tKey('wizard.step3.soap.label'),
|
|
||||||
};
|
|
||||||
|
|
||||||
var CHECK_SVG = '<svg width="10" height="10"><use href="' + (window.APP_BASE||'') + 'icons/general/check.svg#icon"/></svg>';
|
|
||||||
var ARROW_SVG = '<svg width="13" height="13"><use href="' + (window.APP_BASE||'') + 'icons/general/arrow-right.svg#icon"/></svg>';
|
|
||||||
var BACK_SVG = '<svg width="13" height="13"><use href="' + (window.APP_BASE||'') + 'icons/general/arrow-left.svg#icon"/></svg>';
|
|
||||||
|
|
||||||
function goToStep(n) {
|
|
||||||
if (n < 1 || n > TOTAL_STEPS) return;
|
|
||||||
loadStep(n, function() { _doGoToStep(n); });
|
|
||||||
}
|
|
||||||
|
|
||||||
function _doGoToStep(n) {
|
|
||||||
document.querySelectorAll('.step-number[data-step]').forEach(function(element) {
|
|
||||||
var step = Number(element.getAttribute('data-step')) || 0;
|
|
||||||
element.textContent = tfKey('wizard.step_short', { step: step });
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1. hide all panes, show target (step 4 is protocol-specific)
|
|
||||||
document.querySelectorAll('.step-pane').forEach(function(p) { p.style.display = 'none'; });
|
|
||||||
var panelId = (n === 3) ? step3PanelId() : 'step-panel-' + n;
|
|
||||||
var pane = document.getElementById(panelId);
|
|
||||||
if (pane) pane.style.display = '';
|
|
||||||
|
|
||||||
// 2. update sidebar step items
|
|
||||||
document.querySelectorAll('.step-item').forEach(function(item, i) {
|
|
||||||
var num = i + 1;
|
|
||||||
item.classList.remove('active', 'done', 'pending');
|
|
||||||
var ind = item.querySelector('.step-indicator');
|
|
||||||
var statusEl = item.querySelector('.step-status-text');
|
|
||||||
if (num < n) {
|
|
||||||
item.classList.add('done');
|
|
||||||
if (ind) ind.innerHTML = CHECK_SVG;
|
|
||||||
if (statusEl) statusEl.textContent = tKey('wizard.status.completed');
|
|
||||||
} else if (num === n) {
|
|
||||||
item.classList.add('active');
|
|
||||||
if (ind) ind.textContent = num;
|
|
||||||
if (statusEl) statusEl.textContent = tKey('wizard.status.in_progress');
|
|
||||||
} else {
|
|
||||||
item.classList.add('pending');
|
|
||||||
if (ind) ind.textContent = num;
|
|
||||||
if (statusEl) statusEl.textContent = tKey('wizard.status.not_started');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 3. update progress bar
|
|
||||||
var pct = Math.round((n / TOTAL_STEPS) * 100);
|
|
||||||
var fill = document.querySelector('.progress-bar-fill');
|
|
||||||
if (fill) fill.style.width = pct + '%';
|
|
||||||
var pctEl = document.querySelector('.progress-pct');
|
|
||||||
if (pctEl) pctEl.textContent = pct + '%';
|
|
||||||
|
|
||||||
// 4. update step counter
|
|
||||||
var counter = document.querySelector('[data-step-counter]');
|
|
||||||
if (counter) counter.innerHTML = tfKey('wizard.step_of', { step: n, total: TOTAL_STEPS });
|
|
||||||
|
|
||||||
// 5. back button
|
|
||||||
var backBtn = document.querySelector('.btn-back');
|
|
||||||
if (backBtn) {
|
|
||||||
if (n === 1) {
|
|
||||||
backBtn.disabled = true;
|
|
||||||
backBtn.style.opacity = '0.35';
|
|
||||||
backBtn.style.cursor = 'not-allowed';
|
|
||||||
} else {
|
|
||||||
backBtn.disabled = false;
|
|
||||||
backBtn.style.opacity = '';
|
|
||||||
backBtn.style.cursor = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. continue button
|
|
||||||
var contBtn = document.querySelector('.btn-continue');
|
|
||||||
if (contBtn) {
|
|
||||||
if (n === TOTAL_STEPS) {
|
|
||||||
var finalLabel = wizardMode === 'edit' ? tKey('wizard.button.save_changes') : tKey('wizard.button.create');
|
|
||||||
contBtn.innerHTML = finalLabel + ' ' + ARROW_SVG;
|
|
||||||
contBtn.style.cssText = 'background:#3fb950;border-color:#2ea043;box-shadow:0 1px 4px rgba(63,185,80,0.4);';
|
|
||||||
} else {
|
|
||||||
contBtn.innerHTML = tKey('wizard.button.continue') + ' ' + ARROW_SVG;
|
|
||||||
contBtn.style.cssText = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentStep = n;
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadWizardPanels(steps) {
|
|
||||||
return steps.reduce(function(chain, step) {
|
|
||||||
return chain.then(function() {
|
|
||||||
return new Promise(function(resolve) {
|
|
||||||
loadStep(step, resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, Promise.resolve());
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindProtocolCards() {
|
|
||||||
document.querySelectorAll('.protocol-card').forEach(function(card) {
|
|
||||||
card.addEventListener('click', function() {
|
|
||||||
document.querySelectorAll('.protocol-card').forEach(function(item) {
|
|
||||||
item.classList.remove('selected');
|
|
||||||
item.setAttribute('aria-checked', 'false');
|
|
||||||
});
|
|
||||||
card.classList.add('selected');
|
|
||||||
card.setAttribute('aria-checked', 'true');
|
|
||||||
|
|
||||||
var proto = card.dataset.protocol
|
|
||||||
|| (card.classList.contains('rest')
|
|
||||||
? 'rest'
|
|
||||||
: card.classList.contains('graphql')
|
|
||||||
? 'graphql'
|
|
||||||
: card.classList.contains('grpc')
|
|
||||||
? 'grpc'
|
|
||||||
: card.classList.contains('websocket')
|
|
||||||
? 'websocket'
|
|
||||||
: card.classList.contains('soap')
|
|
||||||
? 'soap'
|
|
||||||
: 'rest');
|
|
||||||
|
|
||||||
wizardProtocol = proto;
|
|
||||||
var s3name = document.getElementById('sidebar-step-3-name');
|
|
||||||
if (s3name) s3name.textContent = STEP3_LABELS[proto] || tKey('wizard.step3.rest.label');
|
|
||||||
|
|
||||||
if (proto === 'graphql') {
|
|
||||||
var pathInput = document.getElementById('endpoint-path');
|
|
||||||
if (pathInput && pathInput.value === '/v1/leads') pathInput.value = '/graphql';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
card.addEventListener('keydown', function(event) {
|
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
|
||||||
event.preventDefault();
|
|
||||||
card.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async function() {
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
document.querySelector('.btn-continue').addEventListener('click', function() {
|
document.querySelector('.btn-continue').addEventListener('click', function() {
|
||||||
@@ -2066,7 +1837,10 @@ function selectProtocol(protocol) {
|
|||||||
card.setAttribute('aria-checked', selected ? 'true' : 'false');
|
card.setAttribute('aria-checked', selected ? 'true' : 'false');
|
||||||
});
|
});
|
||||||
var s3name = document.getElementById('sidebar-step-3-name');
|
var s3name = document.getElementById('sidebar-step-3-name');
|
||||||
if (s3name) s3name.textContent = STEP3_LABELS[wizardProtocol] || tKey('wizard.step3.rest.label');
|
if (s3name) {
|
||||||
|
var step3Labels = window.CrankWizardState.step3Labels();
|
||||||
|
s3name.textContent = step3Labels[wizardProtocol] || tKey('wizard.step3.rest.label');
|
||||||
|
}
|
||||||
updateWizardProtocolVisibility();
|
updateWizardProtocolVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user