feat: enhance event page - logo, clickable speaker, add-to-calendar
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

- Norda Biznes logo on featured events
- Speaker name links to person profile
- Google Calendar and ICS/Outlook export buttons
- Update dev banner: official launch April 9, 2026

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-12 09:49:36 +01:00
parent 9fba103729
commit 5dcc99e322
3 changed files with 156 additions and 8 deletions

View File

@ -142,9 +142,21 @@ def event(event_id):
EventAttendee.user_id == current_user.id
).first()
# Find speaker's person_id if speaker has a linked company
speaker_person_id = None
if event.speaker_company_id:
from database import User
speaker_user = db.query(User).filter(
User.company_id == event.speaker_company_id,
User.person_id.isnot(None)
).first()
if speaker_user:
speaker_person_id = speaker_user.person_id
return render_template('calendar/event.html',
event=event,
user_attending=user_attending
user_attending=user_attending,
speaker_person_id=speaker_person_id,
)
finally:
db.close()

View File

@ -719,12 +719,12 @@
* DEVELOPMENT NOTICE BANNER
* ============================================================ */
.dev-notice {
background: linear-gradient(135deg, #fef3c7, #fde68a);
border-bottom: 2px solid #f59e0b;
background: linear-gradient(135deg, #dbeafe, #bfdbfe);
border-bottom: 2px solid #3b82f6;
padding: var(--spacing-sm) var(--spacing-md);
text-align: center;
font-size: var(--font-size-sm);
color: #92400e;
color: #1e3a5f;
display: flex;
align-items: center;
justify-content: center;
@ -742,7 +742,7 @@
.dev-notice-close {
background: none;
border: none;
color: #92400e;
color: #1e3a5f;
font-size: 1.2em;
cursor: pointer;
padding: 0 var(--spacing-sm);
@ -1827,9 +1827,9 @@
<!-- Development Notice Banner -->
<div class="dev-notice" id="devNotice">
<span class="dev-notice-icon">🚧</span>
<span class="dev-notice-icon">🎉</span>
<span class="dev-notice-text">
Serwis w fazie rozwoju. Oficjalne uruchomienie po akceptacji Zarządu Norda Biznes.
Oficjalne uruchomienie portalu: <strong>9 kwietnia 2026</strong> — prezentacja dla członków Izby w Urzędzie Miasta Wejherowo.
</span>
<button class="dev-notice-close" onclick="dismissDevNotice()" aria-label="Zamknij">&times;</button>
</div>

View File

@ -225,6 +225,64 @@
#rsvp-btn.attending {
background: var(--success);
}
.event-logo {
display: flex;
align-items: center;
gap: var(--spacing-md);
margin-bottom: var(--spacing-lg);
}
.event-logo img {
height: 48px;
width: auto;
}
.speaker-link {
color: var(--primary);
text-decoration: none;
font-weight: 500;
}
.speaker-link:hover {
text-decoration: underline;
}
.calendar-add {
display: flex;
gap: var(--spacing-sm);
flex-wrap: wrap;
margin-top: var(--spacing-md);
padding-top: var(--spacing-md);
border-top: 1px solid var(--border-color, #e5e7eb);
}
.calendar-add-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: var(--radius);
font-size: var(--font-size-sm);
font-weight: 500;
text-decoration: none;
transition: var(--transition);
border: 1px solid var(--border-color, #e5e7eb);
background: var(--surface);
color: var(--text-primary);
cursor: pointer;
}
.calendar-add-btn:hover {
background: var(--background);
border-color: var(--primary);
color: var(--primary);
}
.calendar-add-btn svg {
width: 16px;
height: 16px;
}
</style>
{% endblock %}
@ -237,6 +295,11 @@
</a>
<div class="event-header">
{% if event.is_featured %}
<div class="event-logo">
<img src="{{ url_for('static', filename='img/norda-logo.svg') }}" alt="Norda Biznes">
</div>
{% endif %}
<h1>{{ event.title }}
{% if event.access_level == 'admin_only' %}
<span style="display:inline-block;background:#ef4444;color:#fff;font-size:12px;padding:2px 8px;border-radius:4px;font-weight:600;vertical-align:middle;">UKRYTE</span>
@ -301,12 +364,32 @@
</svg>
<div>
<div class="info-label">Prelegent</div>
<div class="info-value">{{ event.speaker_name }}</div>
<div class="info-value">
{% if speaker_person_id %}
<a href="{{ url_for('person_detail', person_id=speaker_person_id) }}" class="speaker-link">{{ event.speaker_name }}</a>
{% else %}
{{ event.speaker_name }}
{% endif %}
</div>
</div>
</div>
{% endif %}
</div>
{% if event.time_start and not event.is_past %}
<div class="calendar-add">
<span style="font-size: var(--font-size-sm); color: var(--text-secondary); align-self: center;">Dodaj do kalendarza:</span>
<a href="#" class="calendar-add-btn" onclick="addToGoogleCalendar(); return false;">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.5 3h-15A1.5 1.5 0 003 4.5v15A1.5 1.5 0 004.5 21h15a1.5 1.5 0 001.5-1.5v-15A1.5 1.5 0 0019.5 3zM12 17.25a.75.75 0 01-.75-.75v-3.75H7.5a.75.75 0 010-1.5h3.75V7.5a.75.75 0 011.5 0v3.75h3.75a.75.75 0 010 1.5h-3.75v3.75a.75.75 0 01-.75.75z"/></svg>
Google Calendar
</a>
<a href="#" class="calendar-add-btn" onclick="downloadICS(); return false;">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.5 3h-15A1.5 1.5 0 003 4.5v15A1.5 1.5 0 004.5 21h15a1.5 1.5 0 001.5-1.5v-15A1.5 1.5 0 0019.5 3zM12 17.25a.75.75 0 01-.75-.75v-3.75H7.5a.75.75 0 010-1.5h3.75V7.5a.75.75 0 011.5 0v3.75h3.75a.75.75 0 010 1.5h-3.75v3.75a.75.75 0 01-.75.75z"/></svg>
Outlook / ICS
</a>
</div>
{% endif %}
{% if event.image_url %}
<div style="margin-bottom: var(--spacing-xl); border-radius: var(--radius-lg); overflow: hidden;">
<img src="{{ event.image_url }}" alt="{{ event.title }}" style="width: 100%; height: auto; display: block;">
@ -449,4 +532,57 @@ async function toggleRSVP() {
btn.disabled = false;
}
/* --- Add to Calendar functions --- */
const _evt = {
title: {{ event.title|tojson }},
date: '{{ event.event_date.strftime("%Y%m%d") }}',
start: '{{ event.time_start.strftime("%H%M") if event.time_start else "0000" }}',
end: '{{ event.time_end.strftime("%H%M") if event.time_end else "" }}',
location: {{ (event.location or '')|tojson }},
url: window.location.href,
};
function addToGoogleCalendar() {
const start = _evt.date + 'T' + _evt.start + '00';
const end = _evt.end ? (_evt.date + 'T' + _evt.end + '00') : '';
const params = new URLSearchParams({
action: 'TEMPLATE',
text: _evt.title,
dates: start + '/' + (end || start),
location: _evt.location,
details: 'Szczegóły: ' + _evt.url,
ctz: 'Europe/Warsaw',
});
window.open('https://calendar.google.com/calendar/render?' + params, '_blank');
}
function downloadICS() {
const start = _evt.date + 'T' + _evt.start + '00';
const end = _evt.end ? (_evt.date + 'T' + _evt.end + '00') : (_evt.date + 'T' + _evt.start + '00');
const uid = 'nordabiz-event-{{ event.id }}@nordabiznes.pl';
const now = new Date().toISOString().replace(/[-:]/g,'').split('.')[0] + 'Z';
const ics = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//NordaBiznes//PL',
'BEGIN:VEVENT',
'UID:' + uid,
'DTSTAMP:' + now,
'DTSTART;TZID=Europe/Warsaw:' + start,
'DTEND;TZID=Europe/Warsaw:' + end,
'SUMMARY:' + _evt.title,
'LOCATION:' + _evt.location,
'DESCRIPTION:Szczegóły: ' + _evt.url,
'URL:' + _evt.url,
'END:VEVENT',
'END:VCALENDAR',
].join('\r\n');
const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = _evt.title.substring(0, 50).replace(/[^a-zA-Z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ ]/g, '') + '.ics';
a.click();
URL.revokeObjectURL(a.href);
}
{% endblock %}