vibe coded maxx

This commit is contained in:
2026-05-22 13:46:02 +02:00
parent 8f7f14c95b
commit 8feb88dfa6
9 changed files with 787 additions and 17 deletions
+170 -16
View File
@@ -66,6 +66,13 @@
<span>Generate Test Plan</span>
</button>
<!-- Export Automated Playwright Tests -->
<button onclick="exportAutomatedTests()"
class="px-4 py-2 bg-[#0b1120] text-orange-400 border border-orange-400 hover:bg-orange-900/30 rounded-lg text-sm font-semibold flex items-center gap-2">
<i class="fa-solid fa-file-code"></i>
<span>Export Automated Tests</span>
</button>
<!-- Load existing Test Plan -->
<button onclick="openLoadTestPlanModal()"
class="px-4 py-2 bg-[#0b1120] text-orange-400 border border-orange-400 hover:bg-orange-900/30 rounded-lg text-sm font-semibold flex items-center gap-2">
@@ -128,6 +135,7 @@
<button onclick="selectAllCore()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded">All Core</button>
<button onclick="selectAllPbx()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded">All PBX</button>
<button onclick="clearSelection()" class="px-2 py-1 bg-[#1e2937] hover:bg-red-600 rounded text-red-300">Clear</button>
<button onclick="selectOnlyFullyAutomated()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded text-orange-300">Only Fully Automated</button>
<span class="ml-3 text-amber-300 font-medium" id="selectionCount"></span>
</div>
@@ -201,16 +209,42 @@
<ul id="listAutomatable" class="editable-list text-sm"></ul>
</div>
<!-- Not Yet -->
<div>
<div class="flex justify-between items-center mb-1.5">
<div class="section-title text-red-400">Not Automatable Yet / Manual</div>
<button onclick="addListItem('notYet')" class="text-xs px-2.5 py-0.5 bg-red-900/60 hover:bg-red-800 rounded">+ Add</button>
</div>
<ul id="listNotYet" class="editable-list text-sm"></ul>
</div>
<!-- Not Yet -->
<div>
<div class="flex justify-between items-center mb-1.5">
<div class="section-title text-red-400">Not Automatable Yet / Manual</div>
<button onclick="addListItem('notYet')" class="text-xs px-2.5 py-0.5 bg-red-900/60 hover:bg-red-800 rounded">+ Add</button>
</div>
<ul id="listNotYet" class="editable-list text-sm"></ul>
</div>
<!-- Dependencies -->
<!-- Code Generation (for automated tests) -->
<div>
<div class="section-title mb-1.5 text-orange-400">Code Generation (Automated)</div>
<div class="flex items-center gap-2 mb-2">
<input type="checkbox" id="fullyAutomated" class="w-4 h-4">
<label for="fullyAutomated" class="text-sm">Mark as <strong>fullyAutomated</strong> (ready for code generation)</label>
</div>
<div class="mb-2">
<div class="text-xs text-[#64748b] mb-1">Implementation Notes (for generator / LLM)</div>
<textarea id="implementationNotes" rows="2"
class="w-full bg-[#0f172a] border border-[#334155] rounded p-2 text-sm"
placeholder="Hints for the code generator..."></textarea>
</div>
<!-- codeSteps editor -->
<div>
<div class="flex justify-between items-center mb-1">
<div class="text-xs text-[#64748b]">Code Steps (for generator)</div>
<button onclick="addCodeStep()" class="text-xs px-2 py-0.5 bg-orange-900/60 hover:bg-orange-800 rounded">+ Add Step</button>
</div>
<div id="codeStepsContainer" class="space-y-2 text-sm"></div>
</div>
</div>
<!-- Dependencies -->
<div>
<div class="flex justify-between items-center mb-1.5">
<div class="section-title">Dependencies &amp; Prerequisites</div>
@@ -417,6 +451,15 @@
updateSelectionCount();
}
function selectOnlyFullyAutomated() {
selectedIds.clear();
allTests.forEach(t => {
if (t.fullyAutomated === true) selectedIds.add(t.id);
});
renderTable();
updateSelectionCount();
}
function clearSelection() {
selectedIds.clear();
renderTable();
@@ -485,20 +528,63 @@
if (m) m.remove();
}
async function exportAutomatedTests() {
const automatedTests = allTests.filter(t => t.fullyAutomated === true || t.status === 'automated');
if (automatedTests.length === 0) {
alert('No fully automated tests found.');
return;
}
// For now we just generate the whole group of the first selected test (simple approach)
const firstGroup = automatedTests[0]?.group;
const confirmed = confirm(`Generate automated tests for group "${firstGroup}"?\n\nFiles will be written to tests/generated/.`);
if (!confirmed) return;
try {
const res = await fetch('/api/export/automated-tests', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ group: firstGroup })
});
const data = await res.json();
if (data.success) {
alert(`✅ Automated tests generated for group "${firstGroup}".\nCheck tests/generated/ folder.\n\nPlease review before committing.`);
} else {
alert('Generation failed: ' + (data.error || 'Unknown error'));
}
} catch (e) {
alert('Error exporting automated tests: ' + e.message);
}
}
function openEditor(globalIndex) {
currentIndex = globalIndex;
currentTest = JSON.parse(JSON.stringify(allTests[globalIndex])); // deep clone
document.getElementById('modalId').textContent = currentTest.id;
document.getElementById('modalGroup').textContent = `${currentTest.group}${currentTest.file}`;
document.getElementById('modalDescription').value = currentTest.description || '';
document.getElementById('modalStatus').value = currentTest.status;
document.getElementById('modalDescription').value = currentTest.description || '';
document.getElementById('modalStatus').value = currentTest.status;
// Render lists
renderList('listSteps', currentTest.steps || []);
renderList('listAutomatable', currentTest.automatable || []);
renderList('listNotYet', currentTest.notYet || []);
renderDependencies(currentTest.dependencies || []);
// Code Generation fields (Phase 1)
const fullyAutomatedEl = document.getElementById('fullyAutomated');
fullyAutomatedEl.checked = !!currentTest.fullyAutomated;
const notesEl = document.getElementById('implementationNotes');
notesEl.value = currentTest.implementationNotes || '';
// Code Steps
renderCodeSteps(currentTest.codeSteps || []);
// Render lists
renderList('listSteps', currentTest.steps || []);
renderList('listAutomatable', currentTest.automatable || []);
renderList('listNotYet', currentTest.notYet || []);
renderDependencies(currentTest.dependencies || []);
document.getElementById('editModal').classList.remove('hidden');
document.getElementById('editModal').classList.add('flex');
@@ -571,6 +657,62 @@
renderDependencies(deps);
}
// === Code Steps Editor (for automated generation) ===
function renderCodeSteps(codeSteps = []) {
const container = document.getElementById('codeStepsContainer');
container.innerHTML = '';
codeSteps.forEach((step, index) => {
const div = document.createElement('div');
div.className = 'flex gap-2 items-center bg-[#0f172a] p-2 rounded';
const paramsValue = step.params ? JSON.stringify(step.params) : '';
div.innerHTML = `
<input placeholder="Step Name" value="${step.stepName || ''}" class="flex-1 text-xs" oninput="updateCodeStep(${index}, 'stepName', this.value)">
<input placeholder="Action Key" value="${step.actionKey || ''}" class="flex-1 text-xs" oninput="updateCodeStep(${index}, 'actionKey', this.value)">
<input placeholder='Params (JSON)' value='${paramsValue}' class="flex-1 text-xs" oninput="updateCodeStepParams(${index}, this.value)">
<button onclick="removeCodeStep(${index})" class="text-red-400 hover:text-red-500 px-1">×</button>
`;
container.appendChild(div);
});
container.dataset.codeSteps = JSON.stringify(codeSteps);
}
function updateCodeStep(index, field, value) {
const container = document.getElementById('codeStepsContainer');
const steps = JSON.parse(container.dataset.codeSteps || '[]');
if (!steps[index]) steps[index] = {};
steps[index][field] = value;
container.dataset.codeSteps = JSON.stringify(steps);
}
function updateCodeStepParams(index, value) {
const container = document.getElementById('codeStepsContainer');
const steps = JSON.parse(container.dataset.codeSteps || '[]');
if (!steps[index]) steps[index] = {};
try {
steps[index].params = value ? JSON.parse(value) : undefined;
} catch (e) {
// keep previous value if JSON is invalid
}
container.dataset.codeSteps = JSON.stringify(steps);
}
function removeCodeStep(index) {
const container = document.getElementById('codeStepsContainer');
const steps = JSON.parse(container.dataset.codeSteps || '[]');
steps.splice(index, 1);
renderCodeSteps(steps);
}
function addCodeStep() {
const container = document.getElementById('codeStepsContainer');
const steps = JSON.parse(container.dataset.codeSteps || '[]');
steps.push({ stepName: '', actionKey: '' });
renderCodeSteps(steps);
}
async function openGenerateTestPlanModal() {
if (selectedIds.size === 0) {
alert('Please select at least one test case using the checkboxes.');
@@ -650,6 +792,18 @@
currentTest.description = document.getElementById('modalDescription').value.trim();
currentTest.status = document.getElementById('modalStatus').value;
// Code Generation fields (Phase 1)
const fullyAutomatedEl = document.getElementById('fullyAutomated');
currentTest.fullyAutomated = fullyAutomatedEl.checked;
const notesEl = document.getElementById('implementationNotes');
currentTest.implementationNotes = notesEl.value.trim() || undefined;
// Code Steps
const codeStepsContainer = document.getElementById('codeStepsContainer');
currentTest.codeSteps = JSON.parse(codeStepsContainer.dataset.codeSteps || '[]')
.filter(s => s.stepName || s.actionKey);
// Collect lists
const stepsUl = document.getElementById('listSteps');
currentTest.steps = JSON.parse(stepsUl.dataset.items || '[]').filter(Boolean);