Add monitoring PF service
This commit is contained in:
138
app/templates/suggest.html
Normal file
138
app/templates/suggest.html
Normal file
@@ -0,0 +1,138 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Похожие — {{ project.title }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
.suggest-card { cursor: pointer; transition: border-color .12s, box-shadow .12s; }
|
||||
.suggest-card:hover { border-color: #adb5bd; }
|
||||
.suggest-card.selected { border-color: #0d6efd; box-shadow: 0 0 0 1px #0d6efd inset; }
|
||||
.suggest-card a { cursor: pointer; }
|
||||
/* Sticky action bar so the submit button is always reachable, esp. on mobile */
|
||||
.bulk-bar {
|
||||
position: sticky; bottom: 0; z-index: 1020;
|
||||
background: #fff; border-top: 1px solid #e9ecef;
|
||||
padding: .6rem .25rem; margin-top: 1rem;
|
||||
box-shadow: 0 -4px 12px rgba(0,0,0,.05);
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.bulk-bar #bulk-submit { flex: 1; }
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="mb-3">
|
||||
<a href="{{ url_path('/projects/' ~ project.id) }}" class="btn btn-sm btn-link px-0">← к проекту</a>
|
||||
</div>
|
||||
|
||||
<h3 class="mb-1">Подсказки похожих объявлений</h3>
|
||||
<p class="text-muted">
|
||||
Ищем объявления в том же здании{% if project.bedrooms is not none %}, {{ project.bedrooms }} спален{% endif %},
|
||||
тип сделки: {{ project.deal_type.value }}.
|
||||
{% if our_permit %}Совпадение по DLD permit (<code>{{ our_permit }}</code>) — это тот же объект, такие показаны первыми. {% endif %}
|
||||
Отметьте подходящие объявления галочкой (можно несколько) и нажмите «Отслеживать выбранные» — они попадут в трекинг.
|
||||
</p>
|
||||
|
||||
<form id="bulk-form" method="post" action="{{ url_path('/projects/' ~ project.id ~ '/listings/bulk') }}">
|
||||
{% for src_key, src_label in [('propertyfinder', 'PropertyFinder'), ('bayut', 'Bayut')] %}
|
||||
<h5 class="mt-4">
|
||||
<span class="src-{{ 'pf' if src_key == 'propertyfinder' else 'bayut' }}">{{ src_label }}</span>
|
||||
{% if src_key == 'bayut' and not bayut_enabled %}
|
||||
<small class="text-muted">— ⏸ временно отключён</small>
|
||||
{% else %}
|
||||
<small class="text-muted">— найдено {{ suggestions[src_key]|length }}</small>
|
||||
{% endif %}
|
||||
</h5>
|
||||
{% if src_key == 'bayut' and not bayut_enabled %}
|
||||
<div class="text-muted small">Bayut перешёл на защищённый рендеринг — поиск и трекинг временно недоступны.</div>
|
||||
{% elif not suggestions[src_key] %}
|
||||
<div class="text-muted small">Ничего не нашли (или площадка заблокировала запрос).</div>
|
||||
{% else %}
|
||||
{% for s in suggestions[src_key] %}
|
||||
{% set same_permit = our_permit and s.permit_number and s.permit_number == our_permit %}
|
||||
<label class="card mb-2 suggest-card{% if same_permit %} border-success{% endif %}">
|
||||
<div class="card-body py-2">
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<input class="form-check-input suggest-check flex-shrink-0 mt-1" type="checkbox"
|
||||
name="urls" value="{{ s.url }}">
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-between gap-1">
|
||||
<div>
|
||||
{% if same_permit %}<span class="badge bg-success">🎯 тот же permit</span> {% endif %}
|
||||
<a href="{{ s.url }}" target="_blank" rel="noopener">{{ s.title or 'без названия' }}</a>
|
||||
<div class="small text-muted">
|
||||
{{ s.agent_name or '—' }} ({{ s.agency_name or '—' }})
|
||||
{% if s.permit_number %} · permit <code>{{ s.permit_number }}</code>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="fw-bold text-sm-end text-nowrap">
|
||||
{% if s.price %}{{ "{:,.0f}".format(s.price).replace(",", " ") }} {{ s.currency or 'AED' }}{% else %}—{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="bulk-bar">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="form-check m-0">
|
||||
<input class="form-check-input" type="checkbox" id="select-all">
|
||||
<label class="form-check-label small" for="select-all">Выбрать все</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary ms-auto" id="bulk-submit" disabled>
|
||||
+ Отслеживать выбранные (<span id="sel-count">0</span>)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
(function () {
|
||||
const form = document.getElementById('bulk-form');
|
||||
if (!form) return;
|
||||
const checks = Array.from(form.querySelectorAll('.suggest-check'));
|
||||
const selectAll = document.getElementById('select-all');
|
||||
const countEl = document.getElementById('sel-count');
|
||||
const submitBtn = document.getElementById('bulk-submit');
|
||||
|
||||
function refresh() {
|
||||
let n = 0;
|
||||
checks.forEach(c => {
|
||||
const card = c.closest('.suggest-card');
|
||||
if (card) card.classList.toggle('selected', c.checked);
|
||||
if (c.checked) n++;
|
||||
});
|
||||
countEl.textContent = n;
|
||||
submitBtn.disabled = n === 0;
|
||||
if (selectAll) selectAll.checked = n > 0 && n === checks.length;
|
||||
}
|
||||
|
||||
checks.forEach(c => c.addEventListener('change', refresh));
|
||||
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener('change', () => {
|
||||
checks.forEach(c => { c.checked = selectAll.checked; });
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
|
||||
// Confirm before submitting a large batch — each add re-fetches the page.
|
||||
form.addEventListener('submit', e => {
|
||||
const n = checks.filter(c => c.checked).length;
|
||||
if (n === 0) { e.preventDefault(); return; }
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'Добавляем…';
|
||||
});
|
||||
|
||||
refresh();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user