- event.html: Powrót, wziąć udział, zapisało się, Wezmę udział - index.html: Następny, Zarządzaj, wydarzeń, uczestników - admin.html: Zarządzanie wydarzeniami, wydarzeń
289 lines
9.3 KiB
HTML
Executable File
289 lines
9.3 KiB
HTML
Executable File
{% extends "base.html" %}
|
||
|
||
{% block title %}{{ event.title }} - Norda Biznes Hub{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.event-header {
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.event-header h1 {
|
||
font-size: var(--font-size-3xl);
|
||
color: var(--text-primary);
|
||
margin-bottom: var(--spacing-sm);
|
||
}
|
||
|
||
.event-detail {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-lg);
|
||
padding: var(--spacing-xl);
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.event-info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: var(--spacing-lg);
|
||
margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--spacing-sm);
|
||
}
|
||
|
||
.info-item svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
color: var(--primary);
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: var(--font-size-sm);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.info-value {
|
||
font-weight: 500;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.event-description {
|
||
margin-bottom: var(--spacing-xl);
|
||
line-height: 1.7;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.rsvp-section {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--spacing-lg);
|
||
padding: var(--spacing-lg);
|
||
background: var(--background);
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
.attendees-section {
|
||
background: var(--surface);
|
||
border-radius: var(--radius-lg);
|
||
padding: var(--spacing-xl);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.attendees-section h2 {
|
||
font-size: var(--font-size-xl);
|
||
margin-bottom: var(--spacing-lg);
|
||
}
|
||
|
||
.attendees-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--spacing-sm);
|
||
}
|
||
|
||
.attendee-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--spacing-xs);
|
||
padding: var(--spacing-xs) var(--spacing-sm);
|
||
background: var(--background);
|
||
border-radius: var(--radius);
|
||
font-size: var(--font-size-sm);
|
||
}
|
||
|
||
.back-link {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--spacing-xs);
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
margin-bottom: var(--spacing-lg);
|
||
}
|
||
|
||
.back-link:hover {
|
||
color: var(--primary);
|
||
}
|
||
|
||
#rsvp-btn.attending {
|
||
background: var(--success);
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<a href="{{ url_for('calendar_index') }}" class="back-link">
|
||
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
||
</svg>
|
||
Powrót do kalendarza
|
||
</a>
|
||
|
||
<div class="event-header">
|
||
<h1>{{ event.title }}</h1>
|
||
</div>
|
||
|
||
<div class="event-detail">
|
||
<div class="event-info-grid">
|
||
<div class="info-item">
|
||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
||
<line x1="16" y1="2" x2="16" y2="6"></line>
|
||
<line x1="8" y1="2" x2="8" y2="6"></line>
|
||
<line x1="3" y1="10" x2="21" y2="10"></line>
|
||
</svg>
|
||
<div>
|
||
<div class="info-label">Data</div>
|
||
<div class="info-value">{{ event.event_date.strftime('%d.%m.%Y') }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if event.time_start %}
|
||
<div class="info-item">
|
||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<circle cx="12" cy="12" r="10"></circle>
|
||
<polyline points="12 6 12 12 16 14"></polyline>
|
||
</svg>
|
||
<div>
|
||
<div class="info-label">Godzina</div>
|
||
<div class="info-value">{{ event.time_start.strftime('%H:%M') }}{% if event.time_end %} - {{ event.time_end.strftime('%H:%M') }}{% endif %}</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if event.location %}
|
||
<div class="info-item">
|
||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
|
||
<circle cx="12" cy="10" r="3"></circle>
|
||
</svg>
|
||
<div>
|
||
<div class="info-label">Miejsce</div>
|
||
<div class="info-value">
|
||
{% if event.location_url %}
|
||
<a href="{{ event.location_url }}" target="_blank">{{ event.location }}</a>
|
||
{% else %}
|
||
{{ event.location }}
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if event.speaker_name %}
|
||
<div class="info-item">
|
||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||
<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>
|
||
<div>
|
||
<div class="info-label">Prelegent</div>
|
||
<div class="info-value">{{ event.speaker_name }}</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if event.description %}
|
||
<div class="event-description">
|
||
{{ event.description|safe }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if not event.is_past %}
|
||
<div class="rsvp-section">
|
||
<div>
|
||
<strong>Chcesz wziąć udział?</strong>
|
||
<p class="text-muted" style="margin: 0;">{{ event.attendee_count }} osób już się zapisało{% if event.max_attendees %} (limit: {{ event.max_attendees }}){% endif %}</p>
|
||
</div>
|
||
<button id="rsvp-btn" class="btn {% if user_attending %}btn-secondary attending{% else %}btn-primary{% endif %}" onclick="toggleRSVP()">
|
||
{% if user_attending %}Wypisz się{% else %}Wezmę udział{% endif %}
|
||
</button>
|
||
</div>
|
||
{% else %}
|
||
<div class="rsvp-section" style="background: var(--border);">
|
||
<p class="text-muted" style="margin: 0;">To wydarzenie już się odbyło. {{ event.attendee_count }} osób brało udział.</p>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if event.attendees %}
|
||
<div class="attendees-section">
|
||
<h2>Uczestnicy ({{ event.attendee_count }})</h2>
|
||
<div class="attendees-list">
|
||
{% for attendee in event.attendees %}
|
||
<span class="attendee-badge">
|
||
{{ attendee.user.name or attendee.user.email.split('@')[0] }}
|
||
</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div id="toastContainer" style="position: fixed; top: 80px; right: 20px; z-index: 1100; display: flex; flex-direction: column; gap: 10px;"></div>
|
||
|
||
<style>
|
||
.toast { padding: 12px 20px; border-radius: var(--radius); background: var(--surface); border-left: 4px solid var(--primary); box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px; animation: toastIn 0.3s ease; }
|
||
.toast.success { border-left-color: var(--success); }
|
||
.toast.error { border-left-color: var(--error); }
|
||
@keyframes toastIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; } }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
const csrfToken = '{{ csrf_token() }}';
|
||
|
||
function showToast(message, type = 'info', duration = 4000) {
|
||
const container = document.getElementById('toastContainer');
|
||
const icons = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' };
|
||
const toast = document.createElement('div');
|
||
toast.className = `toast ${type}`;
|
||
toast.innerHTML = `<span style="font-size:1.2em">${icons[type]||'ℹ'}</span><span>${message}</span>`;
|
||
container.appendChild(toast);
|
||
setTimeout(() => { toast.style.animation = 'toastOut 0.3s ease forwards'; setTimeout(() => toast.remove(), 300); }, duration);
|
||
}
|
||
|
||
async function toggleRSVP() {
|
||
const btn = document.getElementById('rsvp-btn');
|
||
btn.disabled = true;
|
||
|
||
try {
|
||
const response = await fetch('{{ url_for("calendar_rsvp", event_id=event.id) }}', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRFToken': csrfToken
|
||
}
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
if (data.action === 'added') {
|
||
btn.textContent = 'Wypisz się';
|
||
btn.classList.remove('btn-primary');
|
||
btn.classList.add('btn-secondary', 'attending');
|
||
showToast('Zapisano na wydarzenie!', 'success');
|
||
} else {
|
||
btn.textContent = 'Wezmę udział';
|
||
btn.classList.remove('btn-secondary', 'attending');
|
||
btn.classList.add('btn-primary');
|
||
showToast('Wypisano z wydarzenia', 'info');
|
||
}
|
||
// Refresh page to update attendees list
|
||
setTimeout(() => location.reload(), 1000);
|
||
} else {
|
||
showToast(data.error || 'Wystąpił błąd', 'error');
|
||
}
|
||
} catch (error) {
|
||
showToast('Błąd połączenia', 'error');
|
||
}
|
||
|
||
btn.disabled = false;
|
||
}
|
||
{% endblock %}
|