feat(search): add portal users to search results alongside KRS persons
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

Search by name now finds registered portal users with avatar, company,
and chamber role badge. Deduplicates with KRS person results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-20 14:27:25 +01:00
parent 83e9cafd06
commit 8501eef2f1
2 changed files with 61 additions and 2 deletions

View File

@ -824,10 +824,31 @@ def search():
logger.info(f"Search '{query}': {len(people_results)} people found")
# Also search portal users (not yet in people_results via person_id)
user_results = []
if query and len(query) >= 2:
q = f"%{query}%"
matched_users = db.query(User).filter(
User.is_active == True,
User.is_verified == True,
User.name.ilike(q)
).limit(20).all()
# Exclude users who are already in people_results (via person_id)
people_person_ids = {p.id for p in people_results}
for u in matched_users:
if u.person_id and u.person_id in people_person_ids:
continue # already shown as Person
user_results.append(u)
if user_results:
logger.info(f"Search '{query}': {len(user_results)} portal users found")
return render_template(
'search_results.html',
companies=companies,
people=people_results,
user_results=user_results,
query=query,
category_id=category_id,
result_count=len(companies),

View File

@ -242,7 +242,7 @@
{% if query %}
<p class="search-meta">
Znaleziono <strong>{{ companies|length }}</strong> firm
{%- if people %} i <strong>{{ people|length }}</strong> osób{% endif %}
{%- if people or user_results %} i <strong>{{ (people|length) + (user_results|length) }}</strong> osób{% endif %}
dla zapytania: <span class="search-query">"{{ query }}"</span>
</p>
{% else %}
@ -283,6 +283,44 @@
</div>
{% endif %}
{% if user_results %}
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
</svg>
Użytkownicy portalu ({{ user_results|length }})
</h2>
<div class="people-grid">
{% for user in user_results %}
<a href="{{ url_for('public.user_profile', user_id=user.id) }}" class="person-card">
<div class="person-avatar-small" {% if user.avatar_path %}style="padding: 0; overflow: hidden;"{% endif %}>
{% if user.avatar_path %}
<img src="{{ url_for('static', filename=user.avatar_path) }}" style="width: 100%; height: 100%; object-fit: cover;">
{% else %}
{{ (user.name or user.email)[0].upper() }}
{% endif %}
</div>
<div class="person-info">
<div class="person-name-link">
{{ user.name or user.email.split('@')[0] }}
{% if user.chamber_role_label %}
<span style="font-size: 10px; background: {{ '#fef3c7' if user.chamber_role == 'prezes' else '#dbeafe' if user.chamber_role == 'wiceprezes' else '#ecfdf5' }}; color: {{ '#92400e' if user.chamber_role == 'prezes' else '#1d4ed8' if user.chamber_role == 'wiceprezes' else '#065f46' }}; padding: 1px 6px; border-radius: 4px; font-weight: 600; vertical-align: middle; margin-left: 4px;">{{ user.chamber_role_label }}</span>
{% endif %}
<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">
{% if user.company %}{{ user.company.name }}{% else %}Członek portalu{% endif %}
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
{% if companies %}
<div class="companies-grid">
{% for company in companies %}
@ -341,7 +379,7 @@
</div>
{% endif %}
{% if not companies and not people %}
{% if not companies and not people and not user_results %}
<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"/>