188 lines
7.7 KiB
HTML
188 lines
7.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Flowbroker - Create Flow</title>
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<style>
|
|
.step, .requirement {
|
|
border: 1px solid #ddd;
|
|
padding: 10px;
|
|
margin-bottom: 15px;
|
|
border-radius: 4px;
|
|
background-color: #f9f9f9;
|
|
}
|
|
.step h3, .step h4, .requirement h5 {
|
|
margin-top: 0;
|
|
}
|
|
.step .requirementsContainer {
|
|
margin-left: 20px;
|
|
border-left: 2px solid #007bff;
|
|
padding-left: 15px;
|
|
}
|
|
button.removeStepBtn, button.removeRequirementBtn {
|
|
background-color: #dc3545;
|
|
margin-top: 5px;
|
|
}
|
|
button.removeStepBtn:hover, button.removeRequirementBtn:hover {
|
|
background-color: #c82333;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Create New Flow</h1>
|
|
<form id="createFlowForm" action="/flows" method="post">
|
|
<div>
|
|
<label for="flow_name">Flow Name:</label>
|
|
<input type="text" id="flow_name" name="flow_name" required>
|
|
</div>
|
|
<hr>
|
|
|
|
<div id="stepsContainer">
|
|
<!-- Steps will be added here by JavaScript -->
|
|
</div>
|
|
|
|
<button type="button" id="addStepBtn" class="addBtn">Add Step</button>
|
|
<hr>
|
|
<button type="submit">Create Flow</button>
|
|
</form>
|
|
<p><a href="/">Back to Flows List</a></p>
|
|
|
|
<!-- Template for a new step -->
|
|
<template id="stepTemplate">
|
|
<div class="step" data-step-index="">
|
|
<h3>Step <span class="step-number"></span></h3>
|
|
<button type="button" class="removeStepBtn">Remove This Step</button>
|
|
<div>
|
|
<label>Step Description (Optional):</label>
|
|
<input type="text" name="steps[X].description" class="step-description">
|
|
</div>
|
|
<h4>Signature Requirements for Step <span class="step-number"></span></h4>
|
|
<div class="requirementsContainer" data-step-index="">
|
|
<!-- Requirements will be added here -->
|
|
</div>
|
|
<button type="button" class="addRequirementBtn addBtn" data-step-index="">Add Signature Requirement</button>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Template for a new signature requirement -->
|
|
<template id="requirementTemplate">
|
|
<div class="requirement" data-req-index="">
|
|
<h5>Requirement <span class="req-number"></span></h5>
|
|
<button type="button" class="removeRequirementBtn">Remove Requirement</button>
|
|
<div>
|
|
<label>Message to Sign:</label>
|
|
<textarea name="steps[X].requirements[Y].message" rows="2" required class="req-message"></textarea>
|
|
</div>
|
|
<div>
|
|
<label>Required Public Key:</label>
|
|
<input type="text" name="steps[X].requirements[Y].public_key" required class="req-pubkey">
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const stepsContainer = document.getElementById('stepsContainer');
|
|
const addStepBtn = document.getElementById('addStepBtn');
|
|
const stepTemplate = document.getElementById('stepTemplate');
|
|
const requirementTemplate = document.getElementById('requirementTemplate');
|
|
const form = document.getElementById('createFlowForm');
|
|
|
|
const updateIndices = () => {
|
|
const steps = stepsContainer.querySelectorAll('.step');
|
|
steps.forEach((step, stepIdx) => {
|
|
// Update step-level attributes and text
|
|
step.dataset.stepIndex = stepIdx;
|
|
step.querySelector('.step-number').textContent = stepIdx + 1;
|
|
step.querySelector('.step-description').name = `steps[${stepIdx}].description`;
|
|
|
|
const addReqBtn = step.querySelector('.addRequirementBtn');
|
|
if (addReqBtn) addReqBtn.dataset.stepIndex = stepIdx;
|
|
|
|
const requirements = step.querySelectorAll('.requirementsContainer .requirement');
|
|
requirements.forEach((req, reqIdx) => {
|
|
// Update requirement-level attributes and text
|
|
req.dataset.reqIndex = reqIdx;
|
|
req.querySelector('.req-number').textContent = reqIdx + 1;
|
|
req.querySelector('.req-message').name = `steps[${stepIdx}].requirements[${reqIdx}].message`;
|
|
req.querySelector('.req-pubkey').name = `steps[${stepIdx}].requirements[${reqIdx}].public_key`;
|
|
});
|
|
});
|
|
};
|
|
|
|
const addRequirement = (currentStepElement, stepIndex) => {
|
|
const requirementsContainer = currentStepElement.querySelector('.requirementsContainer');
|
|
const reqFragment = requirementTemplate.content.cloneNode(true);
|
|
const newRequirement = reqFragment.querySelector('.requirement');
|
|
|
|
requirementsContainer.appendChild(newRequirement);
|
|
updateIndices(); // Update all indices after adding
|
|
};
|
|
|
|
const addStep = () => {
|
|
const stepFragment = stepTemplate.content.cloneNode(true);
|
|
const newStep = stepFragment.querySelector('.step');
|
|
stepsContainer.appendChild(newStep);
|
|
|
|
// Add at least one requirement to the new step automatically
|
|
const currentStepIndex = stepsContainer.querySelectorAll('.step').length - 1;
|
|
addRequirement(newStep, currentStepIndex);
|
|
|
|
updateIndices(); // Update all indices after adding
|
|
};
|
|
|
|
// Event delegation for remove buttons and add requirement button
|
|
stepsContainer.addEventListener('click', (event) => {
|
|
if (event.target.classList.contains('removeStepBtn')) {
|
|
event.target.closest('.step').remove();
|
|
if (stepsContainer.querySelectorAll('.step').length === 0) { // Ensure at least one step
|
|
addStep();
|
|
}
|
|
updateIndices();
|
|
} else if (event.target.classList.contains('addRequirementBtn')) {
|
|
const stepElement = event.target.closest('.step');
|
|
const stepIndex = parseInt(stepElement.dataset.stepIndex, 10);
|
|
addRequirement(stepElement, stepIndex);
|
|
} else if (event.target.classList.contains('removeRequirementBtn')) {
|
|
const requirementElement = event.target.closest('.requirement');
|
|
const stepElement = event.target.closest('.step');
|
|
const requirementsContainer = stepElement.querySelector('.requirementsContainer');
|
|
requirementElement.remove();
|
|
// Ensure at least one requirement per step
|
|
if (requirementsContainer.querySelectorAll('.requirement').length === 0) {
|
|
const stepIndex = parseInt(stepElement.dataset.stepIndex, 10);
|
|
addRequirement(stepElement, stepIndex);
|
|
}
|
|
updateIndices();
|
|
}
|
|
});
|
|
|
|
addStepBtn.addEventListener('click', addStep);
|
|
|
|
// Add one step by default when the page loads
|
|
if (stepsContainer.children.length === 0) {
|
|
addStep();
|
|
}
|
|
|
|
// Optional: Validate that there's at least one step and one requirement before submit
|
|
form.addEventListener('submit', (event) => {
|
|
if (stepsContainer.querySelectorAll('.step').length === 0) {
|
|
alert('Please add at least one step to the flow.');
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
const steps = stepsContainer.querySelectorAll('.step');
|
|
for (let i = 0; i < steps.length; i++) {
|
|
if (steps[i].querySelectorAll('.requirementsContainer .requirement').length === 0) {
|
|
alert(`Step ${i + 1} must have at least one signature requirement.`);
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|