feat: deal name field in wizard step 1; inline validation errors on required fields; no more alert() dialogs

This commit is contained in:
James 2026-03-17 16:57:03 -04:00
parent cd2b67edd2
commit 8dfd8c8b1d
1 changed files with 60 additions and 12 deletions

View File

@ -185,6 +185,13 @@
<button onclick="closeAddOrgModal()" class="text-[#b0bec5] hover:text-white transition text-2xl leading-none">&times;</button>
</div>
<p id="addOrgHint" class="text-[#b0bec5] text-sm mb-5">Enter an email address or domain name — we'll look up the organization automatically.</p>
<!-- Project name — only shown in wizard mode -->
<div id="wizProjectNameWrap" class="hidden mb-4">
<label class="block text-xs text-[#b0bec5] mb-1">Deal name <span class="text-red-400">*</span></label>
<input type="text" id="wizProjectName" placeholder="e.g. Project Falcon"
class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]"
onkeydown="if(event.key==='Enter')scrapeOrg()">
</div>
<div class="flex gap-3">
<input type="text" id="addOrgEmail" placeholder="e.g. john.smith@example.com or example.com" autocomplete="off"
class="flex-1 px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]"
@ -266,7 +273,8 @@
</div>
<div class="flex justify-between">
<button onclick="showAddOrgStep(1)" class="px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition">Back</button>
<button onclick="validateStep2() && showAddOrgStep(3)" class="px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Next: Select People</button>
<div id="step2Error" class="hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm">Please fill in all required fields.</div>
<button onclick="advanceFromStep2()" class="px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Next: Select People</button>
</div>
</div>
@ -302,7 +310,10 @@
</div>
<div class="flex justify-between">
<button onclick="showAddOrgStep(2)" class="px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition">Back</button>
<button onclick="proceedFromStep3()" id="addOrgSubmitBtn" class="px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Add to Deal</button>
<div class="flex flex-col items-end gap-2">
<div id="step3Error" class="hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm text-right"></div>
<button onclick="proceedFromStep3()" id="addOrgSubmitBtn" class="px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Add to Deal</button>
</div>
</div>
</div>
@ -314,12 +325,6 @@
</div>
<p class="text-[#b0bec5] text-sm mb-4">Choose which request list templates to include in this project.</p>
<div>
<label class="block text-xs mb-1" style="color:var(--ds-tx2)">Project name</label>
<input type="text" id="wizProjectName" class="w-full px-3 py-2 rounded-lg text-sm focus:outline-none mb-4"
style="background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)">
</div>
<div class="space-y-3 mb-6" id="templateList">
<label class="flex items-start gap-3 p-4 rounded-xl cursor-pointer transition" style="background:var(--ds-hv);border:1px solid var(--ds-bd)">
<input type="checkbox" class="tmpl-cb mt-0.5 accent-[#c9a84c]" value="core-8" checked onchange="tmplChanged(this)">
@ -346,6 +351,7 @@
<div class="flex justify-between">
<button onclick="showAddOrgStep(3)" class="px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition">Back</button>
<div class="w-full mb-2"><div id="wizError" class="hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm"></div></div>
<button onclick="createNewProject()" id="wizCreateBtn" class="px-6 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Create Project</button>
</div>
</div>
@ -1271,6 +1277,9 @@
document.getElementById('scrapeError').classList.add('hidden');
document.getElementById('scrapeLoading').classList.add('hidden');
document.getElementById('scrapeBtn').disabled = false;
// Hide wizard-only fields
document.getElementById('wizProjectNameWrap').classList.add('hidden');
document.getElementById('wizProjectName').value = '';
// Step 2 — clear all org fields
const clearIds = ['orgName','orgDesc','orgRole','orgIndustry','orgWebsite','orgPhone',
'orgAddress','orgCity','orgState','orgFounded','orgLinkedIn'];
@ -1320,8 +1329,19 @@
async function createNewProject() {
const btn = document.getElementById('wizCreateBtn');
const projectName = document.getElementById('wizProjectName').value.trim();
if (!projectName) { alert('Project name is required.'); return; }
if (!validateStep2()) { showAddOrgStep(2); return; }
const wizErr = document.getElementById('wizError');
if (wizErr) wizErr.classList.add('hidden');
const projNameEl = document.getElementById('wizProjectName');
if (!projectName) {
if (projNameEl) projNameEl.classList.add('field-error');
if (wizErr) { wizErr.textContent = 'Deal name is required.'; wizErr.classList.remove('hidden'); }
return;
}
if (projNameEl) projNameEl.classList.remove('field-error');
if (!validateStep2()) {
if (wizErr) { wizErr.textContent = 'Please fill in all required org fields. Go back to step 2.'; wizErr.classList.remove('hidden'); }
return;
}
btn.disabled = true; btn.textContent = 'Creating...';
@ -1440,6 +1460,8 @@
document.getElementById('addOrgStep2').classList.toggle('hidden', n !== 2);
document.getElementById('addOrgStep3').classList.toggle('hidden', n !== 3);
document.getElementById('addOrgStep4').classList.toggle('hidden', n !== 4);
// Clear error banners when navigating
['step2Error','step3Error','wizError'].forEach(id => { const el=document.getElementById(id); if(el) el.classList.add('hidden'); });
if (n === 3) renderPeople();
if (n === 4) {
// Pre-fill project name from org name
@ -1466,8 +1488,29 @@
return requiredFields.every(id => document.getElementById(id).value.trim());
}
function showStep2Error(msg) {
const el = document.getElementById('step2Error');
if (!el) return;
el.textContent = msg || 'Please fill in all required fields.';
el.classList.remove('hidden');
}
function hideStep2Error() {
const el = document.getElementById('step2Error');
if (el) el.classList.add('hidden');
}
function advanceFromStep2() {
if (!validateStep2()) {
showStep2Error('Please fill in all required fields (highlighted in red).');
return;
}
hideStep2Error();
showAddOrgStep(3);
}
// Clear red border on input/change
requiredFields.forEach(id => {
[...requiredFields, 'wizProjectName'].forEach(id => {
const el = document.getElementById(id);
if (el) {
const clear = () => el.classList.remove('field-error');
@ -1623,7 +1666,12 @@
}
async function submitAddOrg() {
if (!validateStep2()) { showAddOrgStep(2); return; }
const s3err = document.getElementById('step3Error');
if (s3err) s3err.classList.add('hidden');
if (!validateStep2()) {
if (s3err) { s3err.textContent = 'Required fields missing: go back and fill in Name and Role.'; s3err.classList.remove('hidden'); }
return;
}
const name = document.getElementById('orgName').value.trim();
const role = document.getElementById('orgRole').value;