feat: enrich person card with board badge, activity, events, forum stats
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
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
- Rada Izby badge next to name - Last active label (e.g. "Aktywny 2 dni temu") - Forum stats (topics + replies count) - Recent events attended (up to 5, linked) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
12eb506a93
commit
da5f93368f
@ -396,10 +396,60 @@ def person_detail(person_id):
|
||||
))
|
||||
portal_user = candidates[0]
|
||||
|
||||
# Extra data if portal user exists
|
||||
is_rada_member = False
|
||||
last_active_label = None
|
||||
attended_events = []
|
||||
forum_topics_count = 0
|
||||
forum_replies_count = 0
|
||||
|
||||
if portal_user:
|
||||
is_rada_member = getattr(portal_user, 'is_rada_member', False)
|
||||
|
||||
# Last activity label
|
||||
from datetime import datetime, timedelta
|
||||
if portal_user.last_login:
|
||||
diff = datetime.now() - portal_user.last_login
|
||||
if diff < timedelta(hours=1):
|
||||
last_active_label = 'Aktywny teraz'
|
||||
elif diff < timedelta(days=1):
|
||||
hours = int(diff.total_seconds() // 3600)
|
||||
last_active_label = f'Aktywny {hours} godz. temu'
|
||||
elif diff < timedelta(days=7):
|
||||
days = diff.days
|
||||
last_active_label = f'Aktywny {days} dni temu'
|
||||
elif diff < timedelta(days=60):
|
||||
weeks = diff.days // 7
|
||||
last_active_label = f'Aktywny {weeks} tyg. temu'
|
||||
else:
|
||||
last_active_label = f'Ostatnio: {portal_user.last_login.strftime("%d.%m.%Y")}'
|
||||
|
||||
# Events attended
|
||||
from database import EventAttendee, NordaEvent, ForumTopic, ForumReply
|
||||
attended_events = db.query(NordaEvent).join(
|
||||
EventAttendee, EventAttendee.event_id == NordaEvent.id
|
||||
).filter(
|
||||
EventAttendee.user_id == portal_user.id,
|
||||
EventAttendee.status == 'confirmed',
|
||||
).order_by(NordaEvent.event_date.desc()).limit(5).all()
|
||||
|
||||
# Forum stats
|
||||
forum_topics_count = db.query(ForumTopic).filter_by(
|
||||
author_id=portal_user.id, is_deleted=False
|
||||
).count()
|
||||
forum_replies_count = db.query(ForumReply).filter_by(
|
||||
author_id=portal_user.id, is_deleted=False
|
||||
).count()
|
||||
|
||||
return render_template('person_detail.html',
|
||||
person=person,
|
||||
company_roles=company_roles,
|
||||
portal_user=portal_user
|
||||
portal_user=portal_user,
|
||||
is_rada_member=is_rada_member,
|
||||
last_active_label=last_active_label,
|
||||
attended_events=attended_events,
|
||||
forum_topics_count=forum_topics_count,
|
||||
forum_replies_count=forum_replies_count,
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@ -239,10 +239,18 @@
|
||||
<div class="person-avatar">
|
||||
{{ person.imiona[0] }}{{ person.nazwisko[0] }}
|
||||
</div>
|
||||
<h1 class="person-name">{{ person.full_name() }}</h1>
|
||||
<h1 class="person-name">
|
||||
{{ person.full_name() }}
|
||||
{% if is_rada_member %}
|
||||
<span style="display:inline-block;background:#f59e0b;color:#92400e;font-size:12px;padding:3px 10px;border-radius:20px;font-weight:600;vertical-align:middle;margin-left:8px;">Rada Izby</span>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% set unique_company_ids = company_roles|map(attribute='company_id')|list|unique|list %}
|
||||
<p class="person-subtitle">
|
||||
Powiazany z {{ unique_company_ids|length }} firmami ({{ company_roles|length }} ról) w Norda Biznes
|
||||
Powiązany z {{ unique_company_ids|length }} firmami ({{ company_roles|length }} ról) w Norda Biznes
|
||||
{% if last_active_label %}
|
||||
<span style="display:inline-block;margin-left:12px;padding:2px 10px;background:#ecfdf5;color:#166534;border-radius:12px;font-size:var(--font-size-sm);">{{ last_active_label }}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -331,6 +339,55 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Activity on portal -->
|
||||
{% if portal_user and (attended_events or forum_topics_count > 0 or forum_replies_count > 0) %}
|
||||
<div class="person-section">
|
||||
<h2 class="section-title">
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
||||
</svg>
|
||||
Aktywność na portalu
|
||||
</h2>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--spacing-md); margin-bottom: var(--spacing-lg);">
|
||||
{% if forum_topics_count > 0 %}
|
||||
<div style="text-align:center; padding: var(--spacing-md); background: var(--background); border-radius: var(--radius-lg);">
|
||||
<div style="font-size: var(--font-size-2xl); font-weight: 700; color: var(--primary);">{{ forum_topics_count }}</div>
|
||||
<div style="font-size: var(--font-size-sm); color: var(--text-secondary);">{{ 'temat' if forum_topics_count == 1 else 'tematów' }} na forum</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if forum_replies_count > 0 %}
|
||||
<div style="text-align:center; padding: var(--spacing-md); background: var(--background); border-radius: var(--radius-lg);">
|
||||
<div style="font-size: var(--font-size-2xl); font-weight: 700; color: var(--primary);">{{ forum_replies_count }}</div>
|
||||
<div style="font-size: var(--font-size-sm); color: var(--text-secondary);">{{ 'odpowiedź' if forum_replies_count == 1 else 'odpowiedzi' }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if attended_events %}
|
||||
<div style="text-align:center; padding: var(--spacing-md); background: var(--background); border-radius: var(--radius-lg);">
|
||||
<div style="font-size: var(--font-size-2xl); font-weight: 700; color: var(--primary);">{{ attended_events|length }}</div>
|
||||
<div style="font-size: var(--font-size-sm); color: var(--text-secondary);">{{ 'wydarzenie' if attended_events|length == 1 else 'wydarzeń' }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if attended_events %}
|
||||
<div style="margin-top: var(--spacing-md);">
|
||||
<div style="font-weight: 600; font-size: var(--font-size-sm); color: var(--text-secondary); margin-bottom: var(--spacing-sm);">Ostatnie wydarzenia:</div>
|
||||
{% for event in attended_events %}
|
||||
<a href="{{ url_for('calendar.calendar_event', event_id=event.id) }}" style="display:flex; align-items:center; gap: var(--spacing-sm); padding: 8px 12px; background: var(--background); border-radius: var(--radius); margin-bottom: 4px; text-decoration:none; color: var(--text-primary); font-size: var(--font-size-sm); transition: var(--transition);"
|
||||
onmouseover="this.style.background='var(--border)'" onmouseout="this.style.background='var(--background)'">
|
||||
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="flex-shrink:0;color:var(--text-secondary);">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
|
||||
</svg>
|
||||
<span>{{ event.title }}</span>
|
||||
<span style="margin-left:auto; color:var(--text-secondary); white-space:nowrap;">{{ event.event_date.strftime('%d.%m.%Y') }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Company Roles -->
|
||||
<div class="person-section">
|
||||
<h2 class="section-title">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user