add branch validation to queue
This commit is contained in:
@@ -138,26 +138,61 @@ def submit_step1():
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# If successful, get available scenarios (mock for now)
|
# If successful, run scenario scan to get available scenarios
|
||||||
scenarios = [
|
print(f"[DEBUG] Running scenario scan...")
|
||||||
'Scenario_1_Basic_Test',
|
scan_cmd = f"sshpass -p '{ssh_password}' ssh {ssh_options} {ssh_user}@{ssh_host} 'python3 TPF/Sensor_hub_repo/Tools/TPF/scenario_scan.py'"
|
||||||
'Scenario_2_Advanced_Test',
|
print(f"[DEBUG] Executing scan command: {scan_cmd.replace(ssh_password, '***')}")
|
||||||
'Scenario_3_Integration_Test',
|
|
||||||
'Scenario_4_Performance_Test',
|
scan_result = subprocess.run(scan_cmd, shell=True, capture_output=True, text=True, timeout=120)
|
||||||
'Scenario_5_Security_Test'
|
|
||||||
]
|
print(f"[DEBUG] Scan result - Return code: {scan_result.returncode}")
|
||||||
|
print(f"[DEBUG] Scan stdout: {scan_result.stdout}")
|
||||||
|
print(f"[DEBUG] Scan stderr: {scan_result.stderr}")
|
||||||
|
|
||||||
|
# Parse the JSON response from scenario scan
|
||||||
|
try:
|
||||||
|
scan_data = json.loads(scan_result.stdout)
|
||||||
|
organized_data = scan_data.get('organized_data', {})
|
||||||
|
scenario_map = scan_data.get('scenario_map', {})
|
||||||
|
|
||||||
|
print(f"[DEBUG] Found {len(scenario_map)} scenarios in {len(organized_data)} categories")
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"[ERROR] Failed to parse scenario scan JSON: {e}")
|
||||||
|
print(f"[ERROR] Raw output: {scan_result.stdout}")
|
||||||
|
# Fallback to mock data if scan fails
|
||||||
|
organized_data = {
|
||||||
|
"drivers": {
|
||||||
|
"diag_protocol_stack": ["diag_protocol_stack_init_test"]
|
||||||
|
},
|
||||||
|
"application_layer": {
|
||||||
|
"data_pool": ["data_pool_init_test"],
|
||||||
|
"actuator_manager": ["actuator_manager_init_test2", "actuator_manager_init_test"],
|
||||||
|
"event_system": ["event_system_init_test"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scenario_map = {
|
||||||
|
"diag_protocol_stack_init_test": "drivers/diag_protocol_stack/test/diag_protocol_stack_init_test.test_scenario.xml",
|
||||||
|
"data_pool_init_test": "application_layer/DP_stack/data_pool/test/data_pool_init_test.test_scenario.xml",
|
||||||
|
"actuator_manager_init_test2": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test2.test_scenario.xml",
|
||||||
|
"actuator_manager_init_test": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test.test_scenario.xml",
|
||||||
|
"event_system_init_test": "application_layer/business_stack/event_system/test/event_system_init_test.test_scenario.xml"
|
||||||
|
}
|
||||||
|
|
||||||
print(f"[DEBUG] Branch validation successful for: {branch_name}")
|
print(f"[DEBUG] Branch validation successful for: {branch_name}")
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'scenarios': scenarios,
|
'organized_data': organized_data,
|
||||||
|
'scenario_map': scenario_map,
|
||||||
'message': f'Branch "{branch_name}" validated successfully',
|
'message': f'Branch "{branch_name}" validated successfully',
|
||||||
'output': full_output,
|
'output': full_output,
|
||||||
'debug': {
|
'debug': {
|
||||||
'clone_returncode': clone_result.returncode,
|
'clone_returncode': clone_result.returncode,
|
||||||
'checkout_returncode': checkout_result.returncode,
|
'checkout_returncode': checkout_result.returncode,
|
||||||
'branch_name': branch_name
|
'scan_returncode': scan_result.returncode if 'scan_result' in locals() else 'N/A',
|
||||||
|
'branch_name': branch_name,
|
||||||
|
'scenarios_found': len(scenario_map)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -182,15 +217,20 @@ def submit_step1():
|
|||||||
@login_required
|
@login_required
|
||||||
def submit_step2_validated():
|
def submit_step2_validated():
|
||||||
branch_name = request.form.get('branch_name')
|
branch_name = request.form.get('branch_name')
|
||||||
scenarios_json = request.form.get('scenarios')
|
organized_data_json = request.form.get('organized_data')
|
||||||
|
scenario_map_json = request.form.get('scenario_map')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
scenarios = json.loads(scenarios_json)
|
organized_data = json.loads(organized_data_json) if organized_data_json else {}
|
||||||
except:
|
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
|
||||||
flash('Invalid scenarios data', 'error')
|
except json.JSONDecodeError:
|
||||||
|
flash('Invalid scenario data', 'error')
|
||||||
return redirect(url_for('jobs.submit'))
|
return redirect(url_for('jobs.submit'))
|
||||||
|
|
||||||
return render_template('jobs/submit_step2.html', branch_name=branch_name, scenarios=scenarios)
|
return render_template('jobs/submit_step2.html',
|
||||||
|
branch_name=branch_name,
|
||||||
|
organized_data=organized_data,
|
||||||
|
scenario_map=scenario_map)
|
||||||
@login_required
|
@login_required
|
||||||
def submit_step2():
|
def submit_step2():
|
||||||
branch_name = request.form.get('branch_name')
|
branch_name = request.form.get('branch_name')
|
||||||
|
|||||||
@@ -623,4 +623,134 @@ body {
|
|||||||
background: #9ca3af;
|
background: #9ca3af;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
}/
|
||||||
|
* Scenario Tree Styles */
|
||||||
|
.scenario-controls {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background: var(--light);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-tree {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
background: white;
|
||||||
|
margin: 20px 0;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-layer {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-stack {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-scenario {
|
||||||
|
margin-left: 40px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-toggle {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-spacer {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-label {
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer-label {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-children {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer-checkbox, .stack-checkbox, .scenario-checkbox {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layer-node:hover {
|
||||||
|
background: rgba(59, 130, 246, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack-node:hover {
|
||||||
|
background: rgba(16, 185, 129, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-node:hover {
|
||||||
|
background: rgba(245, 158, 11, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indeterminate checkbox styling */
|
||||||
|
input[type="checkbox"]:indeterminate {
|
||||||
|
background-color: var(--primary);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:indeterminate::before {
|
||||||
|
content: '−';
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection count styling */
|
||||||
|
#selectionCount {
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
let validatedBranch = null;
|
let validatedBranch = null;
|
||||||
let availableScenarios = [];
|
let organizedData = {};
|
||||||
|
let scenarioMap = {};
|
||||||
|
|
||||||
document.getElementById('branchForm').addEventListener('submit', function(e) {
|
document.getElementById('branchForm').addEventListener('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -101,7 +102,8 @@ function validateBranch() {
|
|||||||
nextBtn.style.display = 'inline-block';
|
nextBtn.style.display = 'inline-block';
|
||||||
|
|
||||||
validatedBranch = branchName;
|
validatedBranch = branchName;
|
||||||
availableScenarios = data.scenarios;
|
organizedData = data.organized_data || {};
|
||||||
|
scenarioMap = data.scenario_map || {};
|
||||||
|
|
||||||
// Log debug info
|
// Log debug info
|
||||||
if (data.debug) {
|
if (data.debug) {
|
||||||
@@ -118,7 +120,8 @@ function validateBranch() {
|
|||||||
nextBtn.style.display = 'none';
|
nextBtn.style.display = 'none';
|
||||||
|
|
||||||
validatedBranch = null;
|
validatedBranch = null;
|
||||||
availableScenarios = [];
|
organizedData = {};
|
||||||
|
scenarioMap = {};
|
||||||
|
|
||||||
// Log debug info for troubleshooting
|
// Log debug info for troubleshooting
|
||||||
console.error('Branch validation failed:', data.error);
|
console.error('Branch validation failed:', data.error);
|
||||||
@@ -143,7 +146,7 @@ function validateBranch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function proceedToStep2() {
|
function proceedToStep2() {
|
||||||
if (!validatedBranch || !availableScenarios.length) {
|
if (!validatedBranch || Object.keys(organizedData).length === 0) {
|
||||||
alert('Please validate the branch first');
|
alert('Please validate the branch first');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -159,11 +162,17 @@ function proceedToStep2() {
|
|||||||
branchInput.value = validatedBranch;
|
branchInput.value = validatedBranch;
|
||||||
form.appendChild(branchInput);
|
form.appendChild(branchInput);
|
||||||
|
|
||||||
const scenariosInput = document.createElement('input');
|
const organizedDataInput = document.createElement('input');
|
||||||
scenariosInput.type = 'hidden';
|
organizedDataInput.type = 'hidden';
|
||||||
scenariosInput.name = 'scenarios';
|
organizedDataInput.name = 'organized_data';
|
||||||
scenariosInput.value = JSON.stringify(availableScenarios);
|
organizedDataInput.value = JSON.stringify(organizedData);
|
||||||
form.appendChild(scenariosInput);
|
form.appendChild(organizedDataInput);
|
||||||
|
|
||||||
|
const scenarioMapInput = document.createElement('input');
|
||||||
|
scenarioMapInput.type = 'hidden';
|
||||||
|
scenarioMapInput.name = 'scenario_map';
|
||||||
|
scenarioMapInput.value = JSON.stringify(scenarioMap);
|
||||||
|
form.appendChild(scenarioMapInput);
|
||||||
|
|
||||||
document.body.appendChild(form);
|
document.body.appendChild(form);
|
||||||
form.submit();
|
form.submit();
|
||||||
@@ -181,7 +190,8 @@ document.getElementById('branch_name').addEventListener('input', function() {
|
|||||||
nextBtn.style.display = 'none';
|
nextBtn.style.display = 'none';
|
||||||
|
|
||||||
validatedBranch = null;
|
validatedBranch = null;
|
||||||
availableScenarios = [];
|
organizedData = {};
|
||||||
|
scenarioMap = {};
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -33,36 +33,210 @@
|
|||||||
<strong>Branch:</strong> {{ branch_name }}
|
<strong>Branch:</strong> {{ branch_name }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" action="{{ url_for('jobs.submit_step2') }}">
|
<form method="POST" action="{{ url_for('jobs.submit_step2') }}" id="scenarioForm">
|
||||||
<input type="hidden" name="branch_name" value="{{ branch_name }}">
|
<input type="hidden" name="branch_name" value="{{ branch_name }}">
|
||||||
|
<input type="hidden" name="scenario_map" value="{{ scenario_map|tojson }}">
|
||||||
|
<input type="hidden" name="selected_scenarios" id="selectedScenariosInput">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="scenario-controls">
|
||||||
<label>
|
<button type="button" class="btn btn-sm" onclick="selectAll()" style="background: var(--success); color: white; margin-right: 10px;">Select All</button>
|
||||||
<input type="checkbox" id="selectAll" onclick="toggleAll(this)">
|
<button type="button" class="btn btn-sm" onclick="deselectAll()" style="background: var(--danger); color: white;">Deselect All</button>
|
||||||
Select All Scenarios
|
<span id="selectionCount" style="margin-left: 20px; font-weight: 600;">0 scenarios selected</span>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkbox-group">
|
<div class="scenario-tree">
|
||||||
|
{% for layer_name, layer_data in organized_data.items() %}
|
||||||
|
<div class="tree-layer">
|
||||||
|
<div class="tree-node layer-node" onclick="toggleLayer('{{ layer_name }}')">
|
||||||
|
<span class="tree-toggle" id="toggle-{{ layer_name }}">▶</span>
|
||||||
|
<input type="checkbox" class="layer-checkbox" id="layer-{{ layer_name }}" onchange="toggleLayerSelection('{{ layer_name }}')">
|
||||||
|
<label for="layer-{{ layer_name }}" class="tree-label layer-label">{{ layer_name.replace('_', ' ').title() }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tree-children" id="children-{{ layer_name }}" style="display: none;">
|
||||||
|
{% for stack_name, scenarios in layer_data.items() %}
|
||||||
|
<div class="tree-stack">
|
||||||
|
<div class="tree-node stack-node" onclick="toggleStack('{{ layer_name }}', '{{ stack_name }}')">
|
||||||
|
<span class="tree-toggle" id="toggle-{{ layer_name }}-{{ stack_name }}">▶</span>
|
||||||
|
<input type="checkbox" class="stack-checkbox" id="stack-{{ layer_name }}-{{ stack_name }}" onchange="toggleStackSelection('{{ layer_name }}', '{{ stack_name }}')">
|
||||||
|
<label for="stack-{{ layer_name }}-{{ stack_name }}" class="tree-label stack-label">{{ stack_name.replace('_', ' ').title() }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tree-children" id="children-{{ layer_name }}-{{ stack_name }}" style="display: none;">
|
||||||
{% for scenario in scenarios %}
|
{% for scenario in scenarios %}
|
||||||
<div class="checkbox-item">
|
<div class="tree-scenario">
|
||||||
<input type="checkbox" name="scenarios" value="{{ scenario }}" id="scenario_{{ loop.index }}">
|
<div class="tree-node scenario-node">
|
||||||
<label for="scenario_{{ loop.index }}">{{ scenario }}</label>
|
<span class="tree-spacer"></span>
|
||||||
|
<input type="checkbox" class="scenario-checkbox" id="scenario-{{ scenario }}" value="{{ scenario }}" onchange="updateSelectionCount()" data-layer="{{ layer_name }}" data-stack="{{ stack_name }}">
|
||||||
|
<label for="scenario-{{ scenario }}" class="tree-label scenario-label">{{ scenario }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<a href="{{ url_for('jobs.submit') }}" class="btn" style="background: #6b7280; color: white;">Back</a>
|
<a href="{{ url_for('jobs.submit') }}" class="btn" style="background: #6b7280; color: white;">Back</a>
|
||||||
<button type="submit" class="btn btn-primary">Next</button>
|
<button type="submit" class="btn btn-primary" id="nextBtn" disabled>Next</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function toggleAll(checkbox) {
|
let selectedScenarios = new Set();
|
||||||
const checkboxes = document.querySelectorAll('input[name="scenarios"]');
|
|
||||||
checkboxes.forEach(cb => cb.checked = checkbox.checked);
|
function toggleLayer(layerName) {
|
||||||
|
const children = document.getElementById(`children-${layerName}`);
|
||||||
|
const toggle = document.getElementById(`toggle-${layerName}`);
|
||||||
|
|
||||||
|
if (children.style.display === 'none') {
|
||||||
|
children.style.display = 'block';
|
||||||
|
toggle.textContent = '▼';
|
||||||
|
} else {
|
||||||
|
children.style.display = 'none';
|
||||||
|
toggle.textContent = '▶';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStack(layerName, stackName) {
|
||||||
|
const children = document.getElementById(`children-${layerName}-${stackName}`);
|
||||||
|
const toggle = document.getElementById(`toggle-${layerName}-${stackName}`);
|
||||||
|
|
||||||
|
if (children.style.display === 'none') {
|
||||||
|
children.style.display = 'block';
|
||||||
|
toggle.textContent = '▼';
|
||||||
|
} else {
|
||||||
|
children.style.display = 'none';
|
||||||
|
toggle.textContent = '▶';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLayerSelection(layerName) {
|
||||||
|
const layerCheckbox = document.getElementById(`layer-${layerName}`);
|
||||||
|
const stackCheckboxes = document.querySelectorAll(`input[id^="stack-${layerName}-"]`);
|
||||||
|
const scenarioCheckboxes = document.querySelectorAll(`input[data-layer="${layerName}"]`);
|
||||||
|
|
||||||
|
// Update all stacks and scenarios in this layer
|
||||||
|
stackCheckboxes.forEach(checkbox => {
|
||||||
|
checkbox.checked = layerCheckbox.checked;
|
||||||
|
checkbox.indeterminate = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
scenarioCheckboxes.forEach(checkbox => {
|
||||||
|
checkbox.checked = layerCheckbox.checked;
|
||||||
|
if (layerCheckbox.checked) {
|
||||||
|
selectedScenarios.add(checkbox.value);
|
||||||
|
} else {
|
||||||
|
selectedScenarios.delete(checkbox.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSelectionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStackSelection(layerName, stackName) {
|
||||||
|
const stackCheckbox = document.getElementById(`stack-${layerName}-${stackName}`);
|
||||||
|
const scenarioCheckboxes = document.querySelectorAll(`input[data-layer="${layerName}"][data-stack="${stackName}"]`);
|
||||||
|
|
||||||
|
// Update all scenarios in this stack
|
||||||
|
scenarioCheckboxes.forEach(checkbox => {
|
||||||
|
checkbox.checked = stackCheckbox.checked;
|
||||||
|
if (stackCheckbox.checked) {
|
||||||
|
selectedScenarios.add(checkbox.value);
|
||||||
|
} else {
|
||||||
|
selectedScenarios.delete(checkbox.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateLayerState(layerName);
|
||||||
|
updateSelectionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLayerState(layerName) {
|
||||||
|
const layerCheckbox = document.getElementById(`layer-${layerName}`);
|
||||||
|
const stackCheckboxes = document.querySelectorAll(`input[id^="stack-${layerName}-"]`);
|
||||||
|
const scenarioCheckboxes = document.querySelectorAll(`input[data-layer="${layerName}"]`);
|
||||||
|
|
||||||
|
// Update stack states
|
||||||
|
stackCheckboxes.forEach(stackCheckbox => {
|
||||||
|
const stackName = stackCheckbox.id.replace(`stack-${layerName}-`, '');
|
||||||
|
const stackScenarios = document.querySelectorAll(`input[data-layer="${layerName}"][data-stack="${stackName}"]`);
|
||||||
|
const checkedCount = Array.from(stackScenarios).filter(cb => cb.checked).length;
|
||||||
|
|
||||||
|
if (checkedCount === 0) {
|
||||||
|
stackCheckbox.checked = false;
|
||||||
|
stackCheckbox.indeterminate = false;
|
||||||
|
} else if (checkedCount === stackScenarios.length) {
|
||||||
|
stackCheckbox.checked = true;
|
||||||
|
stackCheckbox.indeterminate = false;
|
||||||
|
} else {
|
||||||
|
stackCheckbox.checked = false;
|
||||||
|
stackCheckbox.indeterminate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update layer state
|
||||||
|
const checkedScenarios = Array.from(scenarioCheckboxes).filter(cb => cb.checked).length;
|
||||||
|
if (checkedScenarios === 0) {
|
||||||
|
layerCheckbox.checked = false;
|
||||||
|
layerCheckbox.indeterminate = false;
|
||||||
|
} else if (checkedScenarios === scenarioCheckboxes.length) {
|
||||||
|
layerCheckbox.checked = true;
|
||||||
|
layerCheckbox.indeterminate = false;
|
||||||
|
} else {
|
||||||
|
layerCheckbox.checked = false;
|
||||||
|
layerCheckbox.indeterminate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectionCount() {
|
||||||
|
// Update selected scenarios set
|
||||||
|
selectedScenarios.clear();
|
||||||
|
document.querySelectorAll('.scenario-checkbox:checked').forEach(checkbox => {
|
||||||
|
selectedScenarios.add(checkbox.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update all layer states
|
||||||
|
const layers = new Set();
|
||||||
|
document.querySelectorAll('.scenario-checkbox').forEach(checkbox => {
|
||||||
|
layers.add(checkbox.dataset.layer);
|
||||||
|
});
|
||||||
|
layers.forEach(layer => updateLayerState(layer));
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
const count = selectedScenarios.size;
|
||||||
|
document.getElementById('selectionCount').textContent = `${count} scenario${count !== 1 ? 's' : ''} selected`;
|
||||||
|
document.getElementById('nextBtn').disabled = count === 0;
|
||||||
|
|
||||||
|
// Update hidden input
|
||||||
|
document.getElementById('selectedScenariosInput').value = JSON.stringify(Array.from(selectedScenarios));
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAll() {
|
||||||
|
document.querySelectorAll('.scenario-checkbox').forEach(checkbox => {
|
||||||
|
checkbox.checked = true;
|
||||||
|
selectedScenarios.add(checkbox.value);
|
||||||
|
});
|
||||||
|
updateSelectionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deselectAll() {
|
||||||
|
document.querySelectorAll('.scenario-checkbox, .stack-checkbox, .layer-checkbox').forEach(checkbox => {
|
||||||
|
checkbox.checked = false;
|
||||||
|
checkbox.indeterminate = false;
|
||||||
|
});
|
||||||
|
selectedScenarios.clear();
|
||||||
|
updateSelectionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
updateSelectionCount();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user