Initial commit
This commit is contained in:
147
dashboard/js/components/session-form.js
Normal file
147
dashboard/js/components/session-form.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Session creation modal
|
||||
*/
|
||||
|
||||
import { createSession } from '../app.js';
|
||||
|
||||
let overlay;
|
||||
|
||||
export function initSessionForm() {
|
||||
overlay = document.getElementById('modal-overlay');
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h2>New Session</h2>
|
||||
<button class="btn-icon" id="modal-close">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Project Profile (JSON)</label>
|
||||
<textarea id="field-profile" spellcheck="false">{
|
||||
"name": "",
|
||||
"tech_stack": [],
|
||||
"description": ""
|
||||
}</textarea>
|
||||
<div class="error-text hidden" id="err-profile"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Immutable Rules</label>
|
||||
<div class="rules-list" id="rules-list">
|
||||
<div class="rule-row">
|
||||
<input type="text" placeholder="Add a rule..." />
|
||||
<button class="btn btn-sm" id="btn-add-rule">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Metadata (JSON)</label>
|
||||
<textarea id="field-metadata" spellcheck="false">{}</textarea>
|
||||
<div class="error-text hidden" id="err-metadata"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" id="btn-cancel">Cancel</button>
|
||||
<button class="btn btn-primary" id="btn-create">Create Session</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.getElementById('modal-close').addEventListener('click', closeModal);
|
||||
document.getElementById('btn-cancel').addEventListener('click', closeModal);
|
||||
document.getElementById('btn-create').addEventListener('click', handleCreate);
|
||||
document.getElementById('btn-add-rule').addEventListener('click', addRuleRow);
|
||||
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) closeModal();
|
||||
});
|
||||
|
||||
// Format JSON on blur
|
||||
for (const id of ['field-profile', 'field-metadata']) {
|
||||
document.getElementById(id).addEventListener('blur', (e) => {
|
||||
try {
|
||||
const parsed = JSON.parse(e.target.value);
|
||||
e.target.value = JSON.stringify(parsed, null, 2);
|
||||
e.target.nextElementSibling.classList.add('hidden');
|
||||
} catch (err) {
|
||||
e.target.nextElementSibling.textContent = `Invalid JSON: ${err.message}`;
|
||||
e.target.nextElementSibling.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addRuleRow() {
|
||||
const list = document.getElementById('rules-list');
|
||||
const addBtn = document.getElementById('btn-add-rule');
|
||||
const row = document.createElement('div');
|
||||
row.className = 'rule-row';
|
||||
row.innerHTML = `
|
||||
<input type="text" placeholder="Add a rule..." />
|
||||
<button class="btn btn-sm btn-danger remove-rule">−</button>
|
||||
`;
|
||||
row.querySelector('.remove-rule').addEventListener('click', () => row.remove());
|
||||
list.insertBefore(row, addBtn.closest('.rule-row'));
|
||||
}
|
||||
|
||||
function getRules() {
|
||||
const inputs = document.querySelectorAll('#rules-list input');
|
||||
return Array.from(inputs).map(i => i.value.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function validateJSON(id) {
|
||||
const el = document.getElementById(id);
|
||||
try {
|
||||
return JSON.parse(el.value);
|
||||
} catch (err) {
|
||||
const errEl = document.getElementById(`err-${id.replace('field-', '')}`);
|
||||
errEl.textContent = `Invalid JSON: ${err.message}`;
|
||||
errEl.classList.remove('hidden');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCreate() {
|
||||
const profile = validateJSON('field-profile');
|
||||
const metadata = validateJSON('field-metadata');
|
||||
if (profile === null || metadata === null) return;
|
||||
|
||||
const rules = getRules();
|
||||
|
||||
const btn = document.getElementById('btn-create');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Creating...';
|
||||
|
||||
try {
|
||||
await createSession({
|
||||
project_profile: profile,
|
||||
immutable_rules: rules,
|
||||
metadata,
|
||||
});
|
||||
closeModal();
|
||||
resetForm();
|
||||
} catch (e) {
|
||||
alert(`Failed: ${e.message}`);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Create Session';
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('field-profile').value = '{\n "name": "",\n "tech_stack": [],\n "description": ""\n}';
|
||||
document.getElementById('field-metadata').value = '{}';
|
||||
const list = document.getElementById('rules-list');
|
||||
const rows = list.querySelectorAll('.rule-row');
|
||||
rows.forEach((r, i) => { if (i < rows.length - 1) r.remove(); });
|
||||
const lastInput = list.querySelector('input');
|
||||
if (lastInput) lastInput.value = '';
|
||||
}
|
||||
|
||||
export function openSessionForm() {
|
||||
overlay.classList.add('open');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
overlay.classList.remove('open');
|
||||
}
|
||||
Reference in New Issue
Block a user