271 lines
12 KiB
HTML
271 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Select Scenarios - ASF TestArena{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="submit-container">
|
|
<h2 style="margin-bottom: 30px; color: var(--dark);">Select Test Scenarios</h2>
|
|
|
|
<div class="step-indicator">
|
|
<div class="step completed">
|
|
<div class="step-number">✓</div>
|
|
<div class="step-label">Branch</div>
|
|
</div>
|
|
<div class="step active">
|
|
<div class="step-number">2</div>
|
|
<div class="step-label">Scenarios</div>
|
|
</div>
|
|
<div class="step">
|
|
<div class="step-number">3</div>
|
|
<div class="step-label">Review</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="background: #eff6ff; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
|
|
<strong>Branch:</strong> {{ branch_name }}
|
|
</div>
|
|
|
|
<form method="POST" action="{{ url_for('jobs.submit_step2') }}" id="scenarioForm">
|
|
<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="scenario-controls">
|
|
<button type="button" class="btn btn-sm" onclick="selectAll()"
|
|
style="background: var(--success); color: white; margin-right: 10px;">Select All</button>
|
|
<button type="button" class="btn btn-sm" onclick="deselectAll()"
|
|
style="background: var(--danger); color: white;">Deselect All</button>
|
|
<span id="selectionCount" style="margin-left: 20px; font-weight: 600;">0 scenarios selected</span>
|
|
</div>
|
|
|
|
<div class="scenario-tree">
|
|
{% if organized_data %}
|
|
{% 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;">
|
|
{% if layer_data %}
|
|
{% 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;">
|
|
{% if scenarios %}
|
|
{% for scenario in scenarios %}
|
|
<div class="tree-scenario">
|
|
<div class="tree-node scenario-node">
|
|
<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 %}
|
|
{% else %}
|
|
<div class="tree-scenario">
|
|
<div class="tree-node scenario-node">
|
|
<span class="tree-spacer"></span>
|
|
<span class="tree-label scenario-label" style="color: #9ca3af;">No scenarios
|
|
found</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="tree-stack">
|
|
<div class="tree-node stack-node">
|
|
<span class="tree-spacer"></span>
|
|
<span class="tree-label stack-label" style="color: #9ca3af;">No stacks found</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="tree-layer">
|
|
<div class="tree-node layer-node">
|
|
<span class="tree-label layer-label" style="color: #9ca3af;">No scenarios available</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="form-actions">
|
|
<a href="{{ url_for('jobs.submit') }}" class="btn" style="background: #6b7280; color: white;">Back</a>
|
|
<button type="submit" class="btn btn-primary" id="nextBtn" disabled>Next</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
let selectedScenarios = new Set();
|
|
|
|
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>
|
|
{% endblock %} |