feat: Agente-Marketing initial commit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Dashboard — Casa Hunter FL{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1" style="color:var(--primary)"><i class="fas fa-map-marker-alt me-2" style="color:var(--accent)"></i>Oportunidades en Costa de Florida</h2>
|
||||
<p class="text-muted mb-0">Stuart → Vero Beach → Melbourne → Daytona → St. Augustine → Jacksonville • Hasta $200,000</p>
|
||||
</div>
|
||||
<button class="btn btn-accent" onclick="runScan()"><i class="fas fa-search me-2"></i>Buscar Ahora</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card stat-card">
|
||||
<div class="number">{{ total }}</div>
|
||||
<div class="label">Propiedades</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card stat-card">
|
||||
<div class="number">{{ favorites }}</div>
|
||||
<div class="label">Favoritas</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card stat-card">
|
||||
<div class="number">$50K</div>
|
||||
<div class="label">Down Payment</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card stat-card">
|
||||
<div class="number">8</div>
|
||||
<div class="label">Lenders Listos</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top Properties -->
|
||||
<h5 class="fw-bold mb-3" style="color:var(--primary)"><i class="fas fa-star me-2" style="color:var(--accent)"></i>Mejores Oportunidades</h5>
|
||||
{% if properties %}
|
||||
<div class="row g-3 mb-4">
|
||||
{% for p in properties %}
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card h-100">
|
||||
{% if p.image_url %}
|
||||
<img src="{{ p.image_url }}" class="card-img-top" style="height:160px;object-fit:cover;border-radius:12px 12px 0 0" alt="">
|
||||
{% else %}
|
||||
<div style="height:100px;background:linear-gradient(135deg,#1a3a5c,#2d6a9f);border-radius:12px 12px 0 0;display:flex;align-items:center;justify-content:center">
|
||||
<i class="fas fa-home fa-2x text-white opacity-50"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<span class="source-tag">{{ p.source }}</span>
|
||||
<span class="score-badge {% if p.score >= 75 %}score-high{% elif p.score >= 50 %}score-mid{% else %}score-low{% endif %}">
|
||||
Score: {{ p.score }}/100
|
||||
</span>
|
||||
</div>
|
||||
<p class="prop-price mb-1">${{ "{:,.0f}".format(p.price) }}</p>
|
||||
<p class="text-muted small mb-1"><i class="fas fa-map-marker-alt me-1"></i>{{ p.address }}</p>
|
||||
<p class="text-muted small mb-2">{{ p.city }}{% if p.county %}, {{ p.county }} Co.{% endif %}</p>
|
||||
<div class="d-flex gap-2 text-muted small mb-3">
|
||||
{% if p.beds %}<span><i class="fas fa-bed me-1"></i>{{ p.beds }} hab.</span>{% endif %}
|
||||
{% if p.baths %}<span><i class="fas fa-bath me-1"></i>{{ p.baths }} baños</span>{% endif %}
|
||||
{% if p.sqft %}<span><i class="fas fa-ruler-combined me-1"></i>{{ p.sqft }} sf</span>{% endif %}
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/property/{{ p.id }}" class="btn btn-primary btn-sm flex-fill">Ver Detalle</a>
|
||||
{% if p.url %}
|
||||
<a href="{{ p.url }}" target="_blank" class="btn btn-outline-secondary btn-sm"><i class="fas fa-external-link-alt"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<a href="/properties" class="btn btn-outline-primary">Ver todas las propiedades <i class="fas fa-arrow-right ms-1"></i></a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card text-center py-5">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-search fa-3x mb-3" style="color:var(--accent)"></i>
|
||||
<h5>No hay propiedades aún</h5>
|
||||
<p class="text-muted">Haz clic en <strong>Buscar Ahora</strong> para encontrar oportunidades en Florida</p>
|
||||
<button class="btn btn-primary" onclick="runScan()"><i class="fas fa-sync me-2"></i>Iniciar Búsqueda</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Top Lenders -->
|
||||
<h5 class="fw-bold mt-4 mb-3" style="color:var(--primary)"><i class="fas fa-handshake me-2" style="color:var(--accent)"></i>Lenders Recomendados para Tu Perfil</h5>
|
||||
<div class="row g-3">
|
||||
{% for l in top_lenders %}
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="card lender-card h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h6 class="fw-bold mb-0" style="color:var(--primary)">{{ l.name }}</h6>
|
||||
<span class="match-pct">{{ l.match_score }}%</span>
|
||||
</div>
|
||||
<span class="badge bg-light text-dark border mb-2">{{ l.loan_type }}</span>
|
||||
<p class="text-muted small">{{ l.specialties[:100] }}{% if l.specialties|length > 100 %}...{% endif %}</p>
|
||||
<a href="/lenders" class="btn btn-sm btn-primary w-100">Ver Script de Contacto</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if last_scan %}
|
||||
<p class="text-muted text-end small mt-3"><i class="fas fa-clock me-1"></i>Última búsqueda: {{ last_scan.ran_at.strftime('%d/%m/%Y %H:%M') }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Scan progress bar -->
|
||||
<div id="scan-progress" class="mt-3" style="display:none">
|
||||
<div class="alert alert-info d-flex align-items-center gap-2">
|
||||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||||
<span id="scan-msg">Iniciando búsqueda...</span>
|
||||
</div>
|
||||
<div class="progress" style="height:6px">
|
||||
<div id="scan-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" style="width:100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let _scanPoll = null;
|
||||
|
||||
function runScan() {
|
||||
const btn = document.querySelector('[onclick="runScan()"]');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Buscando...';
|
||||
document.getElementById('scan-progress').style.display = 'block';
|
||||
document.getElementById('scan-msg').textContent = 'Abriendo Chrome y buscando propiedades...';
|
||||
|
||||
fetch('/scan', {method:'POST'})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.status === 'already_running') {
|
||||
document.getElementById('scan-msg').textContent = 'Ya hay un scan en progreso...';
|
||||
}
|
||||
// Poll for status every 5 seconds
|
||||
_scanPoll = setInterval(pollScanStatus, 5000);
|
||||
})
|
||||
.catch(e => {
|
||||
document.getElementById('scan-msg').textContent = 'Error iniciando scan: ' + e;
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-search me-2"></i>Buscar Ahora';
|
||||
});
|
||||
}
|
||||
|
||||
function pollScanStatus() {
|
||||
fetch('/scan/status')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('scan-msg').textContent = data.progress || 'Buscando...';
|
||||
if (!data.running) {
|
||||
clearInterval(_scanPoll);
|
||||
const btn = document.querySelector('[onclick="runScan()"]');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-search me-2"></i>Buscar Ahora';
|
||||
if (data.new > 0) {
|
||||
document.getElementById('scan-msg').textContent =
|
||||
`✓ ${data.new} propiedades nuevas encontradas. Recargando...`;
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
document.getElementById('scan-msg').textContent =
|
||||
data.progress || 'Busqueda completada.';
|
||||
document.getElementById('scan-bar').classList.remove('progress-bar-animated');
|
||||
setTimeout(() => {
|
||||
document.getElementById('scan-progress').style.display = 'none';
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user