Dashboard
Logged in as
Workflow
Work Queue
File #AddressClientDueStatus
Recently Opened
File #AddressStatus
Recently Completed
File #AddressDate
Billing
Billing Summary
Production
Open / Unpaid Files
File #ClientAddressBilledPaidBalance
Client Balance Summary
ClientFilesTotal BilledTotal ReceivedOutstanding
Work Queue
File #AddressClientDueStatus
Recently Opened
File #AddressStatus
Recently Completed
File #AddressDate
Billing Summary
Production
Open / Unpaid Files
File #ClientAddressBilledPaidBalance
Client Balance Summary
ClientFilesTotal BilledTotal ReceivedOutstanding
File # Property Address Client Owner County Status Order Date Due Date Billed Paid Balance Actions
Clients & Title Companies
Client / CompanyTypeContactsPhoneEmailActive
Staff
Title Held
Counties
File Statuses
Search Types
Document Types
Staff / Employees
NameTitleEmailPhoneActive
How Title is Held
NameSortActive
Counties
CountyState
File Statuses
StatusTypeSortActive
Search Types
Search TypeActive
Document Types
Document TypeCategory
Email / SMTP Settings

Configure outgoing email for report delivery. Credentials are stored locally in your browser session.

Email Template
`; const w=window.open('','_blank','width=1020,height=920'); if (!w) { alert('Popup blocked. Please allow popups for this page.'); return; } w.document.write(winHTML); w.document.close(); } // ══════════════════════════════════════════════════ // CLIENTS — modal-based 3-tab system // ══════════════════════════════════════════════════ let currentClientId = null; let currentContactId = null; let clientModalTab = 'info'; // Temp contacts held during new-client creation (not yet committed to DB) let pendingContacts = []; let editingPendingIdx = null; function renderClients() { document.getElementById('clients-body').innerHTML = DB.clients.map(c => { const contacts = DB.contacts.filter(ct => ct.ClientID === c.ClientID); const defContact = contacts.find(ct => ct.IsDefault) || contacts[0]; return ` ${c.ClientName} ${c.ClientType||'—'} ${contacts.length} contact${contacts.length!==1?'s':''} ${defContact?''+defContact.ContactName+'':''} ${c.Phone||'—'} ${c.Email||'—'} ${c.IsActive?'Active':'Inactive'} `; }).join('') || 'No clients yet. Click "+ Add Client" to get started.'; } // ── Open client modal ── function openClientDetail(id) { currentClientId = id || null; window._editingClientId = id || null; pendingContacts = []; editingPendingIdx = null; const c = id ? DB.clients.find(x => x.ClientID === id) : null; document.getElementById('client-modal-title').textContent = c ? 'Edit Client: ' + c.ClientName : 'Add New Client'; document.getElementById('client-modal-alert').innerHTML = ''; document.getElementById('inline-contact-form').style.display = 'none'; // Populate Company Info setVal('cd-name', c?.ClientName || ''); setVal('cd-phone', c?.Phone || ''); setVal('cd-email', c?.Email || ''); setVal('cd-address', c?.Address || ''); setVal('cd-notes', c?.Notes || ''); setVal('cd-contact-name', ''); if (c?.ClientType) document.getElementById('cd-type').value = c.ClientType; setChk('cd-active', c ? c.IsActive : true); // Reset check marks ['info','contacts','pricing','portal'].forEach(t => { const el = document.getElementById('ctab-'+t+'-check'); if (el) el.style.display = 'none'; }); // For existing client, pre-populate with existing contacts + pricing if (id) { pendingContacts = DB.contacts.filter(ct => ct.ClientID === id).map(ct => ({...ct})); renderModalContacts(); renderModalPricing(id); // Mark info as complete since existing client already has a name const infoCheck = document.getElementById('ctab-info-check'); if (infoCheck) infoCheck.style.display = ''; } else { pendingContacts = []; renderModalContacts(); renderModalPricingBlank(); } switchClientTab('info'); document.getElementById('client-modal-backdrop').classList.add('open'); } function closeClientModal() { document.getElementById('client-modal-backdrop').classList.remove('open'); pendingContacts = []; editingPendingIdx = null; currentClientId = null; } // ── Tab switching with nav button updates ── function switchClientTab(tab) { clientModalTab = tab; ['info','contacts','pricing','portal'].forEach(t => { const panel = document.getElementById('ctab-'+t); const btn = document.getElementById('ctab-'+t+'-btn'); if (panel) panel.classList.toggle('active', t === tab); if (btn) btn.classList.toggle('active', t === tab); }); const prev = document.getElementById('client-modal-prev'); const next = document.getElementById('client-modal-next'); const save = document.getElementById('client-modal-save'); if (tab === 'info') { prev.style.display = 'none'; next.style.display = ''; next.textContent = 'Next: Contacts →'; save.style.display = 'none'; } else if (tab === 'contacts') { prev.style.display = ''; prev.textContent = '← Back'; next.style.display = ''; next.textContent = 'Next: Services & Pricing →'; save.style.display = 'none'; } else if (tab === 'portal') { prev.style.display = ''; prev.textContent = '← Back'; next.style.display = 'none'; save.style.display = ''; if (window._editingClientId) renderPortalAccessTab(window._editingClientId); else { const b = document.getElementById('portal-access-body'); if(b) b.innerHTML = '
Save the client first, then return here to manage portal access.
'; } } else { prev.style.display = ''; prev.textContent = '← Back'; next.style.display = 'none'; save.style.display = ''; } } // ── Navigate forward/back ── function clientModalNav(dir) { const tabs = ['info','contacts','pricing']; const idx = tabs.indexOf(clientModalTab); if (dir === 1) { // Validate before advancing if (clientModalTab === 'info') { if (!gv('cd-name')) { showClientAlert('Client name is required before continuing.', 'error'); return; } // Mark info complete const check = document.getElementById('ctab-info-check'); if (check) check.style.display = ''; } if (clientModalTab === 'contacts') { const hasContact = pendingContacts.length > 0; const hasDefault = pendingContacts.some(c => c.IsDefault); if (!hasContact) { document.getElementById('contacts-required-msg').style.display = ''; showClientAlert('You must add at least one contact before continuing.', 'error'); return; } if (!hasDefault) { document.getElementById('contacts-required-msg').style.display = ''; showClientAlert('Please set one contact as the default before continuing.', 'error'); return; } document.getElementById('contacts-required-msg').style.display = 'none'; const check = document.getElementById('ctab-contacts-check'); if (check) check.style.display = ''; } } if (idx + dir >= 0 && idx + dir < tabs.length) { switchClientTab(tabs[idx + dir]); } } function showClientAlert(msg, type) { const el = document.getElementById('client-modal-alert'); el.innerHTML = `
${msg}
`; setTimeout(() => { el.innerHTML = ''; }, 4000); } // ── Modal contacts (pending, not yet in DB for new clients) ── function renderModalContacts() { const tbody = document.getElementById('modal-contacts-body'); if (!tbody) return; tbody.innerHTML = pendingContacts.map((c, i) => { const nameParts = (c.ContactName||'').trim().split(' '); const uname = c.PortalUsername || genUsername(nameParts[0], nameParts[nameParts.length-1]); return ` ${c.ContactName} ${c.Role||'—'} ${c.Phone||'—'} ${c.Email||'—'} ${uname} ${c.IsDefault?'Default': ``} ${c.IsActive!==false?'Active':'Inactive'} `; }).join('') || 'No contacts yet. Click "+ Add Contact" above.'; } function setPendingDefault(i) { pendingContacts.forEach((c, idx) => c.IsDefault = idx === i); renderModalContacts(); } function openInlineContactForm(editIdx) { editingPendingIdx = editIdx != null ? editIdx : null; const c = editIdx != null ? pendingContacts[editIdx] : null; document.getElementById('inline-contact-title').textContent = c ? 'Edit Contact' : 'New Contact'; setVal('ict-name', c?.ContactName || ''); setVal('ict-role', c?.Role || ''); setVal('ict-phone', c?.Phone || ''); setVal('ict-email', c?.Email || ''); setChk('ict-default', c?.IsDefault || pendingContacts.length === 0); setChk('ict-active', c?.IsActive !== false); document.getElementById('inline-contact-form').style.display = ''; document.getElementById('ict-name').focus(); } function cancelInlineContact() { document.getElementById('inline-contact-form').style.display = 'none'; editingPendingIdx = null; } function saveInlineContact() { const name = gv('ict-name'); if (!name) { alert('Contact name is required.'); return; } const isDefault = gc('ict-default'); if (isDefault) pendingContacts.forEach(c => c.IsDefault = false); const contact = { ContactName: name, Role: gv('ict-role'), Phone: gv('ict-phone'), Email: gv('ict-email'), IsDefault: isDefault, IsActive: gc('ict-active') }; if (editingPendingIdx != null) { pendingContacts[editingPendingIdx] = { ...pendingContacts[editingPendingIdx], ...contact }; } else { // Auto-set first contact as default if (pendingContacts.length === 0) contact.IsDefault = true; pendingContacts.push(contact); } cancelInlineContact(); renderModalContacts(); document.getElementById('contacts-required-msg').style.display = 'none'; } function editPendingContact(i) { openInlineContactForm(i); } function removePendingContact(i) { pendingContacts.splice(i, 1); renderModalContacts(); } // ── Modal pricing ── function renderModalPricing(clientId) { const pricing = DB.clientPricing.filter(p => p.ClientID === clientId); const types = DB.searchTypes.filter(s => s.IsActive); const rushType = types.find(s => s.IsRushFee); const regular = types.filter(s => !s.IsRushFee); let html = regular.map(st => { const p = pricing.find(x => x.SearchTypeID === st.SearchTypeID) || { IsEnabled: false, Price: '' }; return ` ${st.SearchTypeName}
`; }).join(''); if (rushType) { const rp = pricing.find(x => x.SearchTypeID === rushType.SearchTypeID) || { IsEnabled: true, Price: 75 }; html += ` Rush Fee (auto-applied when Rush Order is checked)
`; } const cancelType = DB.searchTypes.find(s => s.IsActive && s.IsCancellationFee); if (cancelType) { const cp = pricing.find(x => x.SearchTypeID === cancelType.SearchTypeID) || { IsEnabled: true, Price: 150 }; html += ` Cancellation Fee (applied if file is cancelled)
`; } const btdTypeP = DB.searchTypes.find(s => s.IsActive && s.IsBtdFee); if (btdTypeP) { const bp = pricing.find(x => x.SearchTypeID === btdTypeP.SearchTypeID) || { IsEnabled: true, Price: 85 }; html += ` Bring to Date Fee (auto-applied when Bring to Date is checked)
`; } document.getElementById('modal-pricing-body').innerHTML = html; } function renderModalPricingBlank() { const regular = DB.searchTypes.filter(s => s.IsActive && !s.IsRushFee); const rushType = DB.searchTypes.find(s => s.IsActive && s.IsRushFee); const defaults = {'60':350,'40':300,'30':275,'Bring':200,'Two':225,'Name':125,'Tax':100}; let html = regular.map(st => { const price = Object.entries(defaults).find(([k]) => st.SearchTypeName.includes(k))?.[1] || 150; return ` ${st.SearchTypeName}
`; }).join(''); if (rushType) { html += ` Rush Fee (auto-applied when Rush Order is checked)
`; } const cancelTypeB = DB.searchTypes.find(s => s.IsActive && s.IsCancellationFee); if (cancelTypeB) { html += ` Cancellation Fee (applied if file is cancelled)
`; } const btdTypeB = DB.searchTypes.find(s => s.IsActive && s.IsBtdFee); if (btdTypeB) { html += ` Bring to Date Fee (auto-applied when Bring to Date is checked)
`; } document.getElementById('modal-pricing-body').innerHTML = html; } function updatePricingRow(stId) { const enabled = document.getElementById('mp-en-'+stId)?.checked; const priceInput = document.getElementById('mp-price-'+stId); if (priceInput) priceInput.style.opacity = enabled ? '1' : '.4'; document.getElementById('pricing-required-msg').style.display = 'none'; } // ── SAVE CLIENT (final — validates all 3 tabs) ── function saveClientFull() { const name = gv('cd-name'); if (!name) { switchClientTab('info'); showClientAlert('Client name is required.', 'error'); return; } const hasContact = pendingContacts.length > 0; const hasDefault = pendingContacts.some(c => c.IsDefault); if (!hasContact || !hasDefault) { switchClientTab('contacts'); document.getElementById('contacts-required-msg').style.display = ''; showClientAlert('At least one contact with a default must be added.', 'error'); return; } const enabledServices = DB.searchTypes.filter(s => s.IsActive && document.getElementById('mp-en-'+s.SearchTypeID)?.checked); if (!enabledServices.length) { switchClientTab('pricing'); document.getElementById('pricing-required-msg').style.display = ''; showClientAlert('At least one service must be enabled.', 'error'); return; } const clientData = { ClientName: name, ClientType: gv('cd-type'), Phone: gv('cd-phone'), Email: gv('cd-email'), Address: gv('cd-address'), Notes: gv('cd-notes'), IsActive: gc('cd-active') }; if (currentClientId) { // Update existing Object.assign(DB.clients.find(c => c.ClientID === currentClientId), clientData); // Sync contacts: update existing, add new ones pendingContacts.forEach(pc => { if (pc.ContactID) { const existing = DB.contacts.find(c => c.ContactID === pc.ContactID); if (existing) Object.assign(existing, pc); } else { DB.contacts.push({ ContactID: genId('contacts'), ClientID: currentClientId, ...pc }); } }); } else { // Create new currentClientId = genId('clients'); DB.clients.push({ ClientID: currentClientId, ...clientData }); pendingContacts.forEach(pc => { DB.contacts.push({ ContactID: genId('contacts'), ClientID: currentClientId, ...pc }); }); } // Save pricing DB.clientPricing = DB.clientPricing.filter(p => p.ClientID !== currentClientId); DB.searchTypes.filter(s => s.IsActive).forEach(st => { const enabled = document.getElementById('mp-en-'+st.SearchTypeID)?.checked || false; const price = parseFloat(document.getElementById('mp-price-'+st.SearchTypeID)?.value) || 0; DB.clientPricing.push({ PricingID: genId('clientPricing'), ClientID: currentClientId, SearchTypeID: st.SearchTypeID, Price: price, IsEnabled: enabled }); }); closeClientModal(); renderClients(); // Refresh lookups so new client appears in dropdowns const clSel = document.getElementById('o-client-id'); if (clSel) { const newOpt = document.createElement('option'); newOpt.value = currentClientId; newOpt.textContent = clientData.ClientName; clSel.appendChild(newOpt); } showClientAlert('Client saved successfully.', 'success'); } // ── CONTACTS (standalone modal for editing existing clients from contact list) ── function openContactModal(id) { currentContactId = id || null; const c = id ? DB.contacts.find(x => x.ContactID === id) : null; document.getElementById('contact-modal-title').textContent = c ? 'Edit Contact' : 'Add Contact'; setVal('ct-name', c?.ContactName || ''); setVal('ct-role', c?.Role || ''); setVal('ct-phone', c?.Phone || ''); setVal('ct-email', c?.Email || ''); setChk('ct-default', c?.IsDefault || false); setChk('ct-active', c ? c.IsActive : true); document.getElementById('contact-modal-backdrop').classList.add('open'); } function closeContactModal() { document.getElementById('contact-modal-backdrop').classList.remove('open'); } function saveContact() { const name = gv('ct-name'); if (!name) { alert('Contact name is required.'); return; } const isDefault = gc('ct-default'); if (isDefault && currentClientId) DB.contacts.filter(c => c.ClientID === currentClientId).forEach(c => c.IsDefault = false); const data = { ContactName: name, Role: gv('ct-role'), Phone: gv('ct-phone'), Email: gv('ct-email'), IsDefault: isDefault, IsActive: gc('ct-active') }; if (currentContactId) Object.assign(DB.contacts.find(c => c.ContactID === currentContactId), data); else DB.contacts.push({ ContactID: genId('contacts'), ClientID: currentClientId, ...data }); closeContactModal(); renderClients(); } function setDefaultContact(contactId) { const ct = DB.contacts.find(c => c.ContactID === contactId); if (!ct) return; DB.contacts.filter(c => c.ClientID === ct.ClientID).forEach(c => c.IsDefault = false); ct.IsDefault = true; renderClients(); } function toggleContactActive(contactId) { const ct = DB.contacts.find(c => c.ContactID === contactId); if (ct) { ct.IsActive = !ct.IsActive; renderClients(); } } function renderClientContacts() {} // no-op, contacts now in modal function renderClientPricing() {} // no-op, pricing now in modal function savePricing() {} // no-op, handled in saveClientFull // ── QUICK ADD CONTACT (from New File screen) ── function openQuickContact() { const cid = gv('o-client-id'); if (!cid) { alert('Please select a client first.'); return; } setVal('qc-name',''); setVal('qc-role',''); setVal('qc-phone',''); setVal('qc-email',''); setChk('qc-default', false); document.getElementById('quick-contact-modal-backdrop').classList.add('open'); } function closeQuickContact() { document.getElementById('quick-contact-modal-backdrop').classList.remove('open'); } function saveQuickContact() { const name = gv('qc-name'); if (!name) { alert('Contact name is required.'); return; } const cid = parseInt(gv('o-client-id')); const isDefault = gc('qc-default'); if (isDefault) DB.contacts.filter(c => c.ClientID === cid).forEach(c => c.IsDefault = false); const newContact = { ContactID: genId('contacts'), ClientID: cid, ContactName: name, Role: gv('qc-role'), Phone: gv('qc-phone'), Email: gv('qc-email'), IsDefault: isDefault, IsActive: true }; DB.contacts.push(newContact); closeQuickContact(); populateContacts(cid, name); } // ── Aliases ── function openClientModal() { openClientDetail(null); } function saveClientInfo() { saveClientFull(); } function saveClient() { saveClientFull(); } function closeClientDetail() { closeClientModal(); } // ══════════════════════════════════════════════════ // LOOKUPS // ══════════════════════════════════════════════════ function switchLkTab(id) { document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.getElementById(id).classList.add('active'); event.target.classList.add('active'); } function renderLookups() { renderStaffTable(); renderTitleHeldTable(); document.getElementById('counties-body').innerHTML = DB.counties.map(c => `${c.CountyName}${c.StateAbbr}`).join(''); document.getElementById('statuses-body').innerHTML = DB.orderStatuses.map(s => `${s.StatusName}${s.Type||'file'}${s.SortOrder}${s.IsActive?'Yes':'No'}`).join(''); document.getElementById('searchtypes-body').innerHTML = DB.searchTypes.filter(s => !s.IsRushFee && !s.IsCancellationFee).map(s => `${s.SearchTypeName}${s.IsActive?'Active':'Inactive'}`).join(''); document.getElementById('doctypes-body').innerHTML = DB.documentTypes.map(d => `${d.DocTypeName}${d.Category}`).join(''); } function renderTitleHeldTable() { const el = document.getElementById('titleheld-body'); if (!el) return; el.innerHTML = DB.titleHeld.sort((a,b) => a.SortOrder - b.SortOrder).map(t => ` ${t.Name} ${t.SortOrder} ${t.IsActive ? 'Active' : 'Inactive'} `).join('') || 'No entries yet.'; } let editingTitleHeldId = null; function openTitleHeldModal(id) { editingTitleHeldId = id || null; const t = id ? DB.titleHeld.find(x => x.TitleHeldID === id) : null; setVal('th-name', t?.Name || ''); setVal('th-sort', t?.SortOrder || DB.titleHeld.length + 1); setChk('th-active', t ? t.IsActive : true); document.getElementById('titleheld-modal-title').textContent = t ? 'Edit Title Held Option' : 'Add Title Held Option'; document.getElementById('titleheld-modal-backdrop').classList.add('open'); } function closeTitleHeldModal() { document.getElementById('titleheld-modal-backdrop').classList.remove('open'); } function saveTitleHeld() { const name = gv('th-name'); if (!name) { alert('Name is required.'); return; } const data = { Name: name, SortOrder: parseInt(gv('th-sort')) || DB.titleHeld.length + 1, IsActive: gc('th-active') }; if (editingTitleHeldId) { Object.assign(DB.titleHeld.find(t => t.TitleHeldID === editingTitleHeldId), data); } else { DB.titleHeld.push({ TitleHeldID: genId('titleHeld'), ...data }); } closeTitleHeldModal(); renderTitleHeldTable(); // Refresh the dropdown on the deed form if it's open populateTitleHeldDropdown(document.getElementById('d-title-held')?.value); } function toggleTitleHeldActive(id) { const t = DB.titleHeld.find(x => x.TitleHeldID === id); if (t) { t.IsActive = !t.IsActive; renderTitleHeldTable(); } } function populateTitleHeldDropdown(selectedVal) { const sel = document.getElementById('d-title-held'); if (!sel) return; sel.innerHTML = '' + DB.titleHeld.filter(t => t.IsActive).sort((a,b) => a.SortOrder - b.SortOrder) .map(t => ``).join(''); } function renderStaffTable() { const el = document.getElementById('staff-body'); if (!el) return; el.innerHTML = DB.staff.map(s => ` ${s.Name} ${s.Title||'—'} ${s.Email||'—'} ${s.Phone||'—'} ${s.IsActive?'Active':'Inactive'} `).join('') || 'No staff added yet.'; } function populateStaffDropdowns(typedByVal, examinedByVal) { const activeStaff = DB.staff.filter(s => s.IsActive); const opts = '' + activeStaff.map(s => `` ).join(''); const tb = document.getElementById('o-typed-by'); const eb = document.getElementById('o-examined-by'); if (tb) { tb.innerHTML = opts; tb.value = typedByVal || ''; } if (eb) { eb.innerHTML = opts; eb.value = examinedByVal || ''; } } let editingStaffId = null; function openStaffModal(id) { editingStaffId = id || null; const s = id ? DB.staff.find(x => x.StaffID === id) : null; setVal('sf-name', s?.Name || ''); setVal('sf-title', s?.Title || ''); setVal('sf-email', s?.Email || ''); setVal('sf-phone', s?.Phone || ''); setChk('sf-active', s ? s.IsActive : true); document.getElementById('staff-modal-title').textContent = s ? 'Edit Staff Member' : 'Add Staff Member'; document.getElementById('staff-modal-backdrop').classList.add('open'); } function closeStaffModal() { document.getElementById('staff-modal-backdrop').classList.remove('open'); } function saveStaff() { const name = gv('sf-name'); if (!name) { alert('Name is required.'); return; } const data = { Name: name, Title: gv('sf-title'), Email: gv('sf-email'), Phone: gv('sf-phone'), IsActive: gc('sf-active') }; if (editingStaffId) { Object.assign(DB.staff.find(s => s.StaffID === editingStaffId), data); } else { DB.staff.push({ StaffID: genId('staff'), ...data }); } closeStaffModal(); renderStaffTable(); } function toggleStaffActive(id) { const s = DB.staff.find(x => x.StaffID === id); if (s) { s.IsActive = !s.IsActive; renderStaffTable(); } } function addStatus() { const name = prompt('Status name:'); if (name) { DB.orderStatuses.push({ StatusID: genId('orderStatuses'), StatusName: name, SortOrder: DB.orderStatuses.length+1, Type: 'file', IsActive: true }); renderLookups(); } } function addSearchType() { const name = prompt('Search type name:'); if (name) { DB.searchTypes.push({ SearchTypeID: genId('searchTypes'), SearchTypeName: name, IsActive: true }); renderLookups(); } } // ══════════════════════════════════════════════════ // INIT // ══════════════════════════════════════════════════ try { seedData(); } catch(e) { console.error('seedData error:', e); } // Dashboard renders after login — not on page load