feat: Agente-Marketing initial commit

This commit is contained in:
2026-07-03 12:23:34 -04:00
commit 293522436a
52 changed files with 13522 additions and 0 deletions
@@ -0,0 +1,224 @@
{% extends "base.html" %}
{% block title %}{{ prop.address }} — Casa Hunter FL{% endblock %}
{% block content %}
<nav aria-label="breadcrumb" class="mb-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Dashboard</a></li>
<li class="breadcrumb-item"><a href="/properties">Propiedades</a></li>
<li class="breadcrumb-item active">Detalle</li>
</ol>
</nav>
<div class="row g-4">
<!-- Left Column -->
<div class="col-12 col-lg-8">
<div class="card mb-3">
{% if prop.image_url %}
<img src="{{ prop.image_url }}" class="card-img-top" style="height:280px;object-fit:cover;border-radius:12px 12px 0 0" alt="">
{% else %}
<div style="height:180px;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-4x text-white opacity-40"></i>
</div>
{% endif %}
<div class="card-body">
<div class="d-flex flex-wrap justify-content-between align-items-start gap-2 mb-3">
<div>
<span class="source-tag me-2">{{ prop.source }}</span>
<span class="badge bg-secondary">{{ prop.status }}</span>
</div>
<span class="score-badge {% if prop.score >= 75 %}score-high{% elif prop.score >= 50 %}score-mid{% else %}score-low{% endif %} fs-6">
Score: {{ prop.score }}/100
</span>
</div>
<h3 class="prop-price mb-1">${{ "{:,.0f}".format(prop.price) }}</h3>
<p class="text-muted mb-1"><i class="fas fa-map-marker-alt me-1"></i>{{ prop.address }}</p>
<p class="text-muted small mb-3">{{ prop.city }}{% if prop.county %}, {{ prop.county }} County{% endif %}, FL {{ prop.zipcode }}</p>
<div class="row text-center g-2 mb-3">
{% if prop.beds %}
<div class="col-4">
<div class="border rounded p-2">
<i class="fas fa-bed text-muted d-block mb-1"></i>
<strong>{{ prop.beds }}</strong><br><small class="text-muted">Habitaciones</small>
</div>
</div>
{% endif %}
{% if prop.baths %}
<div class="col-4">
<div class="border rounded p-2">
<i class="fas fa-bath text-muted d-block mb-1"></i>
<strong>{{ prop.baths }}</strong><br><small class="text-muted">Baños</small>
</div>
</div>
{% endif %}
{% if prop.sqft %}
<div class="col-4">
<div class="border rounded p-2">
<i class="fas fa-ruler-combined text-muted d-block mb-1"></i>
<strong>{{ prop.sqft }}</strong><br><small class="text-muted">Sq Ft</small>
</div>
</div>
{% endif %}
</div>
<div class="d-flex gap-2 flex-wrap">
{% if prop.url %}
<a href="{{ prop.url }}" target="_blank" class="btn btn-primary"><i class="fas fa-external-link-alt me-2"></i>Ver en {{ prop.source }}</a>
{% endif %}
<button class="btn {% if prop.is_favorite %}btn-warning{% else %}btn-outline-warning{% endif %}" onclick="toggleFav({{ prop.id }}, this)">
<i class="fas fa-star me-1"></i>{% if prop.is_favorite %}Guardada{% else %}Guardar{% endif %}
</button>
</div>
</div>
</div>
<!-- AI Analysis -->
<div class="card mb-3">
<div class="card-body">
<h6 class="fw-bold mb-3" style="color:var(--primary)"><i class="fas fa-robot me-2" style="color:var(--accent)"></i>Análisis con IA (qwen2.5)</h6>
<div id="ai-result">
{% if prop.ai_analysis %}
<div class="action-kit">{{ prop.ai_analysis }}</div>
{% else %}
<p class="text-muted">Aún no se ha analizado esta propiedad.</p>
{% endif %}
</div>
<button class="btn btn-sm btn-primary mt-3" onclick="runAnalysis({{ prop.id }})">
<i class="fas fa-magic me-1"></i>{% if prop.ai_analysis %}Re-analizar{% else %}Analizar con IA{% endif %}
</button>
</div>
</div>
<!-- Notes -->
<div class="card">
<div class="card-body">
<h6 class="fw-bold mb-2" style="color:var(--primary)"><i class="fas fa-sticky-note me-2"></i>Mis Notas</h6>
<textarea class="form-control mb-2" id="notes" rows="3" placeholder="Agrega notas sobre esta propiedad...">{{ prop.notes or '' }}</textarea>
<button class="btn btn-sm btn-outline-primary" onclick="saveNotes({{ prop.id }})"><i class="fas fa-save me-1"></i>Guardar Notas</button>
</div>
</div>
</div>
<!-- Right Column: Action Kit -->
<div class="col-12 col-lg-4">
<!-- Action Kit -->
<div class="card mb-3" style="border-left:4px solid var(--accent)">
<div class="card-body">
<h6 class="fw-bold mb-3" style="color:var(--primary)"><i class="fas fa-clipboard-list me-2" style="color:var(--accent)"></i>Kit de Acción</h6>
<p class="small fw-bold text-muted mb-2">DOCUMENTOS A PREPARAR</p>
<ul class="small mb-3">
<li>Green card o visa + pasaporte</li>
<li>12-24 meses de estados de cuenta bancarios</li>
<li>Comprobante de ingresos (facturas de trabajo)</li>
<li>Carta del landlord confirmando pagos de renta</li>
<li>Carta explicando tu situación laboral (self-employed)</li>
<li>Evidencia de los $50,000 disponibles (bank statement)</li>
</ul>
<p class="small fw-bold text-muted mb-2">PASOS A SEGUIR</p>
<ol class="small mb-3">
<li>Llama a 2-3 lenders de la lista (empieza con Heart Mortgage y Jhenesis)</li>
<li>Pide una pre-qualification letter</li>
<li>Contrata un inspector de propiedades ($300-500)</li>
<li>Si es HUD/Fannie Mae, necesitas un agente de bienes raíces aprobado</li>
<li>Presenta oferta con pre-qual letter y prueba de fondos</li>
</ol>
<div class="bg-light rounded p-2 small">
<strong><i class="fas fa-calculator me-1"></i>Estimado para esta propiedad:</strong><br>
Down payment (25%): <strong>${{ "{:,.0f}".format(prop.price * 0.25) }}</strong><br>
Préstamo estimado: <strong>${{ "{:,.0f}".format(prop.price * 0.75) }}</strong><br>
Cuota aprox (7.5%, 30 años): <strong>${{ "{:,.0f}".format(prop.price * 0.75 * 0.007) }}/mes</strong>
</div>
</div>
</div>
<!-- Recommended Lenders -->
<div class="card">
<div class="card-body">
<h6 class="fw-bold mb-3" style="color:var(--primary)"><i class="fas fa-handshake me-2" style="color:var(--accent)"></i>Lenders para Esta Compra</h6>
{% for l in lenders[:4] %}
<div class="border rounded p-2 mb-2">
<div class="d-flex justify-content-between align-items-center">
<strong class="small">{{ l.name }}</strong>
<span class="badge" style="background:var(--accent);color:#fff">{{ l.match_score }}%</span>
</div>
<small class="text-muted">{{ l.loan_type }}</small><br>
<a href="{{ l.website }}" target="_blank" class="btn btn-xs btn-outline-primary btn-sm mt-1" style="font-size:.7rem;padding:2px 8px">
<i class="fas fa-globe me-1"></i>Visitar
</a>
<button class="btn btn-xs btn-outline-secondary btn-sm mt-1 ms-1" style="font-size:.7rem;padding:2px 8px"
onclick="showScript('{{ l.name|e }}', `{{ l.contact_script|e }}`)">
<i class="fas fa-phone me-1"></i>Script
</button>
</div>
{% endfor %}
<a href="/lenders" class="btn btn-sm btn-outline-primary w-100 mt-1">Ver todos los lenders</a>
</div>
</div>
</div>
</div>
<!-- Script Modal -->
<div class="modal fade" id="scriptModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title fw-bold" id="scriptTitle"></h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p class="text-muted small mb-2">Usa este guión cuando los contactes:</p>
<div class="action-kit" id="scriptContent"></div>
</div>
<div class="modal-footer">
<button onclick="copyScript()" class="btn btn-sm btn-primary"><i class="fas fa-copy me-1"></i>Copiar</button>
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function toggleFav(id, btn) {
fetch(`/property/${id}/favorite`, {method:'POST'})
.then(r=>r.json())
.then(d => {
btn.className = d.is_favorite ? 'btn btn-warning' : 'btn btn-outline-warning';
btn.innerHTML = `<i class="fas fa-star me-1"></i>${d.is_favorite ? 'Guardada' : 'Guardar'}`;
});
}
function runAnalysis(id) {
document.getElementById('ai-result').innerHTML = '<div class="text-center py-3"><div class="spinner-border spinner-border-sm me-2"></div>Analizando con qwen2.5...</div>';
fetch(`/property/${id}/analyze`, {method:'POST'})
.then(r=>r.json())
.then(d => {
const container = document.createElement('div');
container.className = 'action-kit';
container.textContent = d.analysis;
const wrapper = document.getElementById('ai-result');
wrapper.innerHTML = '';
wrapper.appendChild(container);
});
}
function saveNotes(id) {
const notes = document.getElementById('notes').value;
fetch(`/property/${id}/notes`, {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({notes})})
.then(()=>alert('Notas guardadas'));
}
function showScript(name, script) {
document.getElementById('scriptTitle').textContent = 'Script: ' + name;
document.getElementById('scriptContent').textContent = script;
new bootstrap.Modal(document.getElementById('scriptModal')).show();
}
function copyScript() {
navigator.clipboard.writeText(document.getElementById('scriptContent').textContent);
alert('Script copiado al portapapeles');
}
</script>
{% endblock %}