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
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:
parent
9fba103729
commit
5dcc99e322
@ -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()
|
||||
|
||||
@ -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">×</button>
|
||||
</div>
|
||||
|
||||
@ -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 %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user