Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions email-generator-service/ollama_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ async def _detect_available_model(client: httpx.AsyncClient) -> Optional[str]:
if model in available:
return model
except Exception:
pass
logger.warning(
"[Ollama] Could not reach Ollama at %s. "
"Is Ollama installed and running? Install it from https://ollama.com",
OLLAMA_BASE_URL,
)
return None


Expand All @@ -64,7 +68,10 @@ async def generate_observation(product_description: str) -> Optional[str]:
async with httpx.AsyncClient() as client:
model = await _detect_available_model(client)
if model is None:
logger.info("[Ollama] No models available or Ollama not running.")
logger.warning(
"[Ollama] No models available. Run 'ollama pull mistral' to install one, "
"or visit https://ollama.com to set up Ollama. Falling back to static observations."
)
return None

logger.info("[Ollama] Using model '%s' for observation generation.", model)
Expand Down
41 changes: 41 additions & 0 deletions gateway/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,9 @@
<button class="nav-tab" data-tab="stats">Stats</button>
</div>
</div>
<button id="btn-scrape">▶ Trigger Scrape</button>
<button id="btn-export-csv" style="padding:7px 14px;background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:13px;font-weight:500;cursor:pointer;" title="Export jobs as CSV">⬇ Export CSV</button>
</nav>
<div class="nav-right">
<button id="btn-theme" aria-label="Toggle theme">🌙 Dark</button>
<button id="btn-scrape">+ Scrape</button>
Expand Down Expand Up @@ -1612,6 +1615,22 @@ <h3>Keyboard Shortcuts</h3>
const grid = document.getElementById('jobs-grid');
if (!jobs.length) {
grid.innerHTML = `<div class="empty" style="grid-column:1/-1">
<div class="empty-icon">📭</div>
<h3 style="font-size:16px;font-weight:600;color:var(--text);margin-bottom:8px">No jobs found yet</h3>
<p style="margin-bottom:20px;font-size:13px">Follow these steps to start discovering jobs</p>
<div style="display:flex;flex-direction:column;gap:8px;max-width:300px;margin:0 auto 20px;text-align:left">
<div style="padding:10px 14px;border:1px solid var(--border);border-radius:var(--radius);font-size:13px">
<strong>Step 1</strong> — Click "Trigger Scrape" to start crawling job platforms
</div>
<div style="padding:10px 14px;border:1px solid var(--border);border-radius:var(--radius);font-size:13px">
<strong>Step 2</strong> — Wait ~60 seconds for jobs to appear
</div>
<div style="padding:10px 14px;border:1px solid var(--border);border-radius:var(--radius);font-size:13px">
<strong>Step 3</strong> — Use filters above to narrow down by role or stack
</div>
</div>
<button class="btn-accent" onclick="document.getElementById('btn-scrape').click()">▶ Trigger Scrape</button>
</div>`;
<div class="empty-icon">🔍</div>
<p>No jobs found. Trigger a scrape to get started.</p>
</div>`;
Expand Down Expand Up @@ -2238,6 +2257,28 @@ <h4>Emails</h4><div class="skel skel-row"></div>
document.getElementById('modal-close').addEventListener('click', () => scrapeModal.classList.remove('open'));
document.getElementById('modal-cancel').addEventListener('click',() => scrapeModal.classList.remove('open'));
scrapeModal.addEventListener('click', e => { if (e.target === scrapeModal) scrapeModal.classList.remove('open'); });
// CSV Export
document.getElementById('btn-export-csv').addEventListener('click', async () => {
const btn = document.getElementById('btn-export-csv');
btn.textContent = '⏳ Exporting...';
btn.disabled = true;
try {
const res = await fetch('/api/jobs/export/csv');
if (!res.ok) throw new Error('Export failed');
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `jobs_${new Date().toISOString().slice(0,10)}.csv`;
a.click();
URL.revokeObjectURL(url);
} catch (e) {
alert('Export failed: ' + e.message);
} finally {
btn.textContent = '⬇ Export CSV';
btn.disabled = false;
}
});
document.addEventListener('keydown', e => { if (e.key === 'Escape') scrapeModal.classList.remove('open'); });
document.getElementById('btn-scrape').addEventListener('click', openScrapeModal);
document.getElementById('modal-close').addEventListener('click', closeScrapeModal);
Expand Down