feat: make user names and avatars clickable links to profiles
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

Add profile links to usernames and avatars across forum, classifieds,
announcements, company recommendations, board members, and group messages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-04-10 06:23:30 +02:00
parent 39da377065
commit b5ef5b0b32
8 changed files with 27 additions and 23 deletions

View File

@ -390,7 +390,7 @@
</span>
{% if announcement.author %}
<span class="meta-author">
&#128100; {{ announcement.author.name }}
&#128100; <a href="{{ url_for('public.user_profile', user_id=announcement.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ announcement.author.name }}</a>
</span>
{% endif %}
<span class="meta-views">

View File

@ -441,7 +441,7 @@
<div class="members-grid">
{% for member in board_members %}
<div class="member-card">
<a href="{{ url_for('public.user_profile', user_id=member.id) }}" class="member-card" style="text-decoration:none;color:inherit;">
<div class="member-avatar">
{% if member.avatar_path %}
<img src="{{ url_for('static', filename=member.avatar_path) }}" alt="{{ member.name }}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">
@ -455,7 +455,7 @@
<div class="member-company">{{ member.company.name }}</div>
{% endif %}
</div>
</div>
</a>
{% else %}
<p class="empty-state">Brak przypisanych członków Rady.</p>
{% endfor %}

View File

@ -302,7 +302,7 @@
{% if classified.company %}
{{ classified.company.name }}
{% else %}
{{ classified.author.name or classified.author.email.split('@')[0] }}
<a href="{{ url_for('public.user_profile', user_id=classified.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ classified.author.name or classified.author.email.split('@')[0] }}</a>
{% endif %}
</div>
<div class="classified-stats">

View File

@ -710,11 +710,11 @@
{% endif %}
<div class="author-card">
<div class="author-avatar">
<a href="{{ url_for('public.user_profile', user_id=classified.author_id) }}" class="author-avatar" style="text-decoration:none;color:inherit;">
{% if classified.author.avatar_path %}<img src="{{ url_for('static', filename=classified.author.avatar_path) }}" alt="" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">{% else %}{{ (classified.author.name or classified.author.email)[0].upper() }}{% endif %}
</div>
</a>
<div class="author-info">
<div class="author-name">{{ classified.author.name or classified.author.email.split('@')[0] }}</div>
<div class="author-name"><a href="{{ url_for('public.user_profile', user_id=classified.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ classified.author.name or classified.author.email.split('@')[0] }}</a></div>
{% if classified.company %}
<div class="author-company">{{ classified.company.name }}</div>
{% endif %}
@ -798,12 +798,12 @@
{% for q in questions %}
<div class="question-item {% if not q.is_public %}question-hidden{% endif %}" id="question-{{ q.id }}">
<div class="question-header">
<div class="question-avatar" style="{% if not q.author.avatar_path %}background: hsl({{ (q.author_id * 137) % 360 }}, 65%, 50%);{% endif %}">
<a href="{{ url_for('public.user_profile', user_id=q.author_id) }}" class="question-avatar" style="text-decoration:none;color:inherit;{% if not q.author.avatar_path %}background: hsl({{ (q.author_id * 137) % 360 }}, 65%, 50%);{% endif %}">
{% if q.author.avatar_path %}<img src="{{ url_for('static', filename=q.author.avatar_path) }}" alt="" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">{% else %}{{ (q.author.name or q.author.email)[0]|upper }}{% endif %}
</div>
</a>
<div class="question-meta">
<div class="question-author">
{{ q.author.name or q.author.email.split('@')[0] }}
<a href="{{ url_for('public.user_profile', user_id=q.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ q.author.name or q.author.email.split('@')[0] }}</a>
{% if q.author.company %}<span style="color: var(--text-secondary); font-weight: normal;"> - {{ q.author.company.name }}</span>{% endif %}
{% if not q.answer %}<span class="pending-badge">Oczekuje na odpowiedz</span>{% endif %}
</div>
@ -821,7 +821,7 @@
{% if q.answer %}
<div class="answer-box">
<div class="answer-label">Odpowiedz od {{ classified.author.name or classified.author.email.split('@')[0] }}</div>
<div class="answer-label">Odpowiedz od <a href="{{ url_for('public.user_profile', user_id=classified.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ classified.author.name or classified.author.email.split('@')[0] }}</a></div>
<div class="answer-content">{{ q.answer }}</div>
</div>
{% elif classified.author_id == current_user.id %}

View File

@ -3123,13 +3123,13 @@
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-xl); border: 2px solid #e5e7eb;">
<!-- Recommender Header -->
<div style="display: flex; align-items: center; gap: var(--spacing-md); margin-bottom: var(--spacing-lg); padding-bottom: var(--spacing-md); border-bottom: 2px solid var(--border);">
<div style="width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-weight: 700; font-size: var(--font-size-lg);">
<a href="{{ url_for('public.user_profile', user_id=rec.user_id) }}" style="width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-weight: 700; font-size: var(--font-size-lg); text-decoration: none;">
{{ rec.user.first_name[0] if rec.user.first_name else rec.user.email[0].upper() }}
</div>
</a>
<div style="flex: 1;">
<div style="font-weight: 600; font-size: var(--font-size-lg); color: var(--text-primary);">
{{ rec.user.first_name }} {{ rec.user.last_name }}
<a href="{{ url_for('public.user_profile', user_id=rec.user_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ rec.user.first_name }} {{ rec.user.last_name }}</a>
</div>
{% if rec.user.company %}
<a href="{{ url_for('company_detail_by_slug', slug=rec.user.company.slug) }}"

View File

@ -421,7 +421,7 @@
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
{{ topic.author.name or topic.author.email.split('@')[0] }}
<a href="{{ url_for('public.user_profile', user_id=topic.author_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ topic.author.name or topic.author.email.split('@')[0] }}</a>
{% if topic.is_ai_generated %}
<span class="ai-indicator" title="Wygenerowano przez AI">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>

View File

@ -205,6 +205,8 @@
cursor: pointer;
position: relative;
font-weight: 500;
text-decoration: none;
color: inherit;
}
.user-stats-trigger:hover {
@ -1061,9 +1063,9 @@
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
<span class="user-stats-trigger" data-user-id="{{ topic.author_id }}">
<a href="{{ url_for('public.user_profile', user_id=topic.author_id) }}" class="user-stats-trigger user-profile-link" data-user-id="{{ topic.author_id }}">
{{ topic.author.name or topic.author.email.split('@')[0] }}
</span>
</a>
{% if topic.is_ai_generated %}
<span class="ai-indicator" title="Wygenerowano przez AI">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
@ -1173,16 +1175,16 @@
<article class="reply-card {% if reply.is_deleted %}deleted{% endif %}" id="reply-{{ reply.id }}">
<div class="reply-header">
<div class="reply-author">
<div class="reply-avatar">
<a href="{{ url_for('public.user_profile', user_id=reply.author_id) }}" class="reply-avatar user-profile-link" style="text-decoration:none;color:inherit;">
{% if reply.author.avatar_path %}
<img src="{{ url_for('static', filename=reply.author.avatar_path) }}" alt="{{ reply.author.name }}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">
{% else %}
{{ (reply.author.name or reply.author.email)[0].upper() }}
{% endif %}
</div>
<span class="user-stats-trigger" data-user-id="{{ reply.author_id }}">
</a>
<a href="{{ url_for('public.user_profile', user_id=reply.author_id) }}" class="user-stats-trigger user-profile-link" data-user-id="{{ reply.author_id }}">
{{ reply.author.name or reply.author.email.split('@')[0] }}
</span>
</a>
{% if reply.is_ai_generated %}
<span class="ai-indicator" title="Wygenerowano przez AI">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>

View File

@ -391,13 +391,15 @@
<tr>
<td>
<div class="member-cell">
<a href="{{ url_for('public.user_profile', user_id=m.user_id) }}" style="text-decoration:none;color:inherit;">
{% if m.user.avatar_path %}
<img src="{{ url_for('static', filename=m.user.avatar_path) }}" class="member-avatar" alt="">
{% else %}
<div class="member-initial">{{ (m.user.name or m.user.email)[0].upper() }}</div>
{% endif %}
</a>
<div>
<div class="member-info-name">{{ m.user.name or m.user.email.split('@')[0] }}</div>
<div class="member-info-name"><a href="{{ url_for('public.user_profile', user_id=m.user_id) }}" style="color:inherit;text-decoration:none;" onmouseover="this.style.textDecoration='underline'" onmouseout="this.style.textDecoration='none'">{{ m.user.name or m.user.email.split('@')[0] }}</a></div>
{% if m.user.privacy_show_email != False %}
<div class="member-info-email">{{ m.user.email }}</div>
{% endif %}