feat: Add person search to main search - partial name matching
- Search people by imiona/nazwisko with partial match (ILIKE) - Display person cards with avatar, name, company count - Show "Znaleziono X firm i Y osób" - People section appears above companies if matches found Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4705f67d71
commit
6e133686b7
24
app.py
24
app.py
@ -780,7 +780,7 @@ def company_recommend(slug):
|
||||
@app.route('/search')
|
||||
@login_required
|
||||
def search():
|
||||
"""Search companies with advanced matching - requires login"""
|
||||
"""Search companies and people with advanced matching - requires login"""
|
||||
query = request.args.get('q', '')
|
||||
category_id = request.args.get('category', type=int)
|
||||
|
||||
@ -799,9 +799,31 @@ def search():
|
||||
match_types[r.match_type] = match_types.get(r.match_type, 0) + 1
|
||||
logger.info(f"Search '{query}': {len(companies)} results, types: {match_types}")
|
||||
|
||||
# Search people by name (partial match)
|
||||
people_results = []
|
||||
if query and len(query) >= 2:
|
||||
q = f"%{query}%"
|
||||
people_results = db.query(Person).filter(
|
||||
db.or_(
|
||||
Person.imiona.ilike(q),
|
||||
Person.nazwisko.ilike(q),
|
||||
db.func.concat(Person.imiona, ' ', Person.nazwisko).ilike(q)
|
||||
)
|
||||
).limit(20).all()
|
||||
|
||||
# For each person, get their company connections count
|
||||
for person in people_results:
|
||||
person.company_count = len(set(
|
||||
r.company_id for r in person.company_roles
|
||||
if r.company and r.company.status == 'active'
|
||||
))
|
||||
|
||||
logger.info(f"Search '{query}': {len(people_results)} people found")
|
||||
|
||||
return render_template(
|
||||
'search_results.html',
|
||||
companies=companies,
|
||||
people=people_results,
|
||||
query=query,
|
||||
category_id=category_id,
|
||||
result_count=len(companies)
|
||||
|
||||
@ -125,10 +125,88 @@
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
/* People results */
|
||||
.people-section {
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.section-title-small {
|
||||
font-size: var(--font-size-lg);
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.section-title-small svg {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.people-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.person-card {
|
||||
background: white;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-md);
|
||||
box-shadow: var(--shadow);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
text-decoration: none;
|
||||
transition: var(--transition);
|
||||
border-left: 4px solid #667eea;
|
||||
}
|
||||
|
||||
.person-card:hover {
|
||||
box-shadow: var(--shadow-lg);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.person-avatar-small {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.person-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.person-name-link {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.person-meta {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.companies-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.people-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -138,7 +216,9 @@
|
||||
<h1>Wyniki wyszukiwania</h1>
|
||||
{% if query %}
|
||||
<p class="search-meta">
|
||||
Znaleziono <strong>{{ companies|length }}</strong> firm dla zapytania: <span class="search-query">"{{ query }}"</span>
|
||||
Znaleziono <strong>{{ companies|length }}</strong> firm
|
||||
{%- if people %} i <strong>{{ people|length }}</strong> osób{% endif %}
|
||||
dla zapytania: <span class="search-query">"{{ query }}"</span>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="search-meta">
|
||||
@ -147,6 +227,37 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if people %}
|
||||
<div class="people-section">
|
||||
<h2 class="section-title-small">
|
||||
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||
</svg>
|
||||
Osoby ({{ people|length }})
|
||||
</h2>
|
||||
<div class="people-grid">
|
||||
{% for person in people %}
|
||||
<a href="{{ url_for('person_detail', person_id=person.id) }}" class="person-card">
|
||||
<div class="person-avatar-small">
|
||||
{{ person.imiona[0] }}{{ person.nazwisko[0] }}
|
||||
</div>
|
||||
<div class="person-info">
|
||||
<div class="person-name-link">
|
||||
{{ person.imiona }} {{ person.nazwisko }}
|
||||
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="opacity: 0.4;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="person-meta">
|
||||
Powiazany z {{ person.company_count }} firmami
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if companies %}
|
||||
<div class="companies-grid">
|
||||
{% for company in companies %}
|
||||
@ -198,7 +309,9 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
|
||||
{% if not companies and not people %}
|
||||
<div class="empty-state">
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none">
|
||||
<circle cx="60" cy="60" r="50" stroke="currentColor" stroke-width="4"/>
|
||||
@ -209,9 +322,9 @@
|
||||
<h2>Nie znaleziono wyników</h2>
|
||||
<p style="color: var(--text-secondary); margin-bottom: var(--spacing-lg);">
|
||||
{% if query %}
|
||||
Spróbuj użyć innych słów kluczowych lub skorzystaj z AI chatu aby znaleźć odpowiednią firmę.
|
||||
Spróbuj użyć innych słów kluczowych lub skorzystaj z AI chatu aby znaleźć odpowiednią firmę lub osobę.
|
||||
{% else %}
|
||||
Nie znaleziono firm spełniających kryteria wyszukiwania.
|
||||
Nie znaleziono firm ani osób spełniających kryteria wyszukiwania.
|
||||
{% endif %}
|
||||
</p>
|
||||
<div style="display: flex; gap: var(--spacing-md); justify-content: center;">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user