add Firecrawl discovery backend and settings

This commit is contained in:
Marco0300
2026-09-17 23:21:52 +02:00
parent 286673eb1d
commit 650ce399f9
5 changed files with 118 additions and 5 deletions
+7
View File
@@ -34,6 +34,7 @@ function renderTeachAI(){const p=settings.company_profile||{};$('view').innerHTM
function renderPromotions(){const items=Array.isArray(settings.promotions)?settings.promotions:[];$('view').innerHTML=layout('Promotions','Create offers once, then select an active promotion while drafting a prospect email. Inactive promotions stay in your history but cannot be used by the AI.',`<div class="promotions-grid"><article class="panel form-panel"><div class="panel-title"><div><span class="eyebrow">NEW PROMOTION</span><h2>Set up an offer</h2><p>Keep the exact wording, price, dates, and eligibility here so drafts stay accurate.</p></div></div><form id="promotionForm" class="promotion-form"><label>Promotion name<input id="promotionName" required placeholder="Free website rebuild"></label><label>What is included<textarea id="promotionDescription" rows="5" required placeholder="Free rebuild for businesses that move hosting to Gold Plus."></textarea></label><label>Terms or eligibility<textarea id="promotionTerms" rows="4" placeholder="New hosting customers only; confirm scope before sending."></textarea></label><div class="settings-fields"><label>Starts<input id="promotionStarts" type="date"></label><label>Ends<input id="promotionEnds" type="date"></label></div><label class="checkline"><input id="promotionActive" type="checkbox" checked> Active and available for drafts</label><div class="form-row"><button class="primary" type="submit">Save promotion</button><span id="promotionMessage" class="notice"></span></div></form></article><article class="panel"><div class="panel-title"><div><span class="eyebrow">YOUR OFFERS</span><h2>Saved promotions</h2><p>${items.filter(x=>x.active).length} active · ${items.length} total</p></div></div><div class="promotion-list">${items.map(item=>`<div class="promotion-card ${item.active?'active':'inactive'}"><div><div class="promotion-card-head"><b>${esc(item.name)}</b><label class="switch"><input type="checkbox" data-promotion-active="${item.id}" ${item.active?'checked':''}><span>${item.active?'Active':'Inactive'}</span></label></div><p>${esc(item.description)}</p>${item.terms?`<small>Terms: ${esc(item.terms)}</small>`:''}<small>${item.starts_at||item.ends_at?`Valid ${esc(item.starts_at||'now')} → ${esc(item.ends_at||'ongoing')}`:'No dates set'}</small></div><button class="secondary" data-delete-promotion="${item.id}" type="button">Delete</button></div>`).join('')||'<p class="muted">No promotions yet. Add your first offer on the left.</p>'}</div></article></div>`)}
function renderSettings(){
const ai=settings.ai||{};
const discovery=settings.discovery||{};
const maps=settings.maps||{};
const browser=settings.browser_use||{};
const limits=settings.limits||{};
@@ -45,6 +46,7 @@ function renderSettings(){
const options=modelList.map(m=>`<option value="${esc(m.id)}">${esc(m.label||m.id)}${m.free?' · free':''}</option>`).join('')+(selectedModel&&!modelList.some(m=>m.id===selectedModel)?`<option value="${esc(selectedModel)}">${esc(selectedModel)}</option>`:'');
$('view').innerHTML=layout('Settings','All provider, browser, run, and safety controls live here. Daily discovery only asks for the search criteria.',`
<div class="settings-grid">
<article class="panel wide-panel"><div class="panel-title"><div><span class="eyebrow">DISCOVERY SOURCE</span><h2>Where to search for prospects</h2><p>Firecrawl searches the public web and can replace Maps for criteria-first discovery. It does not provide Google place IDs, ratings, coordinates, or guaranteed addresses.</p></div><span class="status ${discovery.backend==='firecrawl'&&discovery.firecrawl_configured?'succeeded':'failed'}">${discovery.backend==='firecrawl'&&discovery.firecrawl_configured?'Firecrawl ready':discovery.backend==='firecrawl'?'Firecrawl needs a key':discovery.backend==='browser'?'Browser search':'Maps worker'}</span></div><form id="discoverySettingsForm" class="settings-fields"><label>Discovery backend<select id="discoveryBackend"><option value="gmaps" ${discovery.backend==='gmaps'?'selected':''}>Google Maps structured data</option><option value="firecrawl" ${discovery.backend==='firecrawl'?'selected':''}>Firecrawl web search</option><option value="browser" ${discovery.backend==='browser'?'selected':''}>Browser fallback</option></select></label><label>Firecrawl API key <input id="firecrawlKey" type="password" placeholder="Leave blank to keep the saved key" autocomplete="new-password"><small>Stored server-side and never returned to the browser.</small></label><div class="form-row"><button class="primary" type="submit">Save discovery settings</button><span id="discoverySettingsMessage" class="notice"></span></div></form></article>
<article class="panel wide-panel">
<div class="panel-title"><div><span class="eyebrow">RUNTIME CONTROLS</span><h2>Browser and discovery timing</h2><p>These values are saved in the local database and apply to new work immediately. Increase the Browser Use timeout when website investigations need more time.</p></div><span class="status ${maps.ready?'succeeded':'failed'}">${maps.ready?'Maps ready':'Maps not connected'}</span></div>
<form id="runtimeSettingsForm" class="runtime-settings-form">
@@ -89,6 +91,11 @@ document.addEventListener('submit',async e=>{
e.preventDefault();const m=$('discoverMessage');$('discoverBtn').disabled=true;m.textContent='Queueing…';
try{const r=await api('/api/discover',{method:'POST',body:JSON.stringify({keywords:$('keywords').value,category:$('category').value,location:$('location').value,max_results:Number($('max-results').value),extra_reviews:$('extra-reviews').checked,include_email:true})});m.textContent=`Run #${r.id} started. View it in Runs.`;setTimeout(route,500)}catch(x){m.textContent=x.message}finally{$('discoverBtn').disabled=false}
}
if(e.target.id==='discoverySettingsForm'){
e.preventDefault();const m=$('discoverySettingsMessage');m.textContent='Saving…';m.className='notice';
try{const discovery={backend:$('discoveryBackend').value};if($('firecrawlKey').value.trim())discovery.firecrawl_api_key=$('firecrawlKey').value.trim();const result=await api('/api/settings',{method:'POST',body:JSON.stringify({discovery})});settings.discovery=result.discovery||settings.discovery;m.textContent='Discovery settings saved.';m.className='notice ok';}
catch(x){m.textContent=x.message;m.className='notice error'}
}
if(e.target.id==='settingsForm'){
e.preventDefault();const m=$('settingsMessage');
try{const ai={AI_PROVIDER:$('aiProvider').value,AI_BASE_URL:$('aiBase').value,AI_MODEL:$('aiModel').value};if($('aiKey').value)ai.AI_API_KEY=$('aiKey').value;await api('/api/settings',{method:'POST',body:JSON.stringify({ai})});m.textContent='AI settings saved.';m.className='notice ok';}catch(x){m.textContent=x.message;m.className='notice error'}