feat(calendar): add attachment support to events
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
Adds file attachment capability to NordaEvent model (attachment_filename, attachment_path columns). Admin can upload PDF/DOCX when creating events. Users see a download link on the event detail page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8a1aad948b
commit
327cb5a790
@ -957,6 +957,19 @@ def admin_calendar_new():
|
|||||||
source='manual'
|
source='manual'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Handle file attachment
|
||||||
|
attachment = request.files.get('attachment')
|
||||||
|
if attachment and attachment.filename:
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import uuid
|
||||||
|
filename = secure_filename(attachment.filename)
|
||||||
|
stored_name = f"{uuid.uuid4().hex}_{filename}"
|
||||||
|
upload_dir = os.path.join('static', 'uploads', 'events')
|
||||||
|
os.makedirs(upload_dir, exist_ok=True)
|
||||||
|
attachment.save(os.path.join(upload_dir, stored_name))
|
||||||
|
event.attachment_filename = filename
|
||||||
|
event.attachment_path = f"static/uploads/events/{stored_name}"
|
||||||
|
|
||||||
db.add(event)
|
db.add(event)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
|||||||
@ -2170,6 +2170,10 @@ class NordaEvent(Base):
|
|||||||
# Media
|
# Media
|
||||||
image_url = Column(String(1000)) # Banner/header image URL
|
image_url = Column(String(1000)) # Banner/header image URL
|
||||||
|
|
||||||
|
# Attachment
|
||||||
|
attachment_filename = Column(String(255)) # Original filename
|
||||||
|
attachment_path = Column(String(1000)) # Server path (static/uploads/events/...)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
speaker_company = relationship('Company')
|
speaker_company = relationship('Company')
|
||||||
creator = relationship('User', foreign_keys=[created_by])
|
creator = relationship('User', foreign_keys=[created_by])
|
||||||
|
|||||||
6
database/migrations/085_event_attachments.sql
Normal file
6
database/migrations/085_event_attachments.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- Migration: 085_event_attachments.sql
|
||||||
|
-- Description: Add attachment support to norda_events (filename + server path)
|
||||||
|
-- Date: 2026-03-18
|
||||||
|
|
||||||
|
ALTER TABLE norda_events ADD COLUMN IF NOT EXISTS attachment_filename VARCHAR(255);
|
||||||
|
ALTER TABLE norda_events ADD COLUMN IF NOT EXISTS attachment_path VARCHAR(1000);
|
||||||
@ -103,7 +103,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-card">
|
<div class="form-card">
|
||||||
<form method="POST" action="{{ url_for('admin.admin_calendar_new') }}">
|
<form method="POST" action="{{ url_for('admin.admin_calendar_new') }}" enctype="multipart/form-data">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -183,6 +183,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="attachment">Załącznik (PDF, DOCX)</label>
|
||||||
|
<input type="file" id="attachment" name="attachment" accept=".pdf,.docx,.doc">
|
||||||
|
<div class="form-hint">Opcjonalny plik do pobrania przez uczestników (np. zaproszenie, agenda)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-primary">Utworz wydarzenie</button>
|
<button type="submit" class="btn btn-primary">Utworz wydarzenie</button>
|
||||||
<a href="{{ url_for('admin.admin_calendar') }}" class="btn btn-secondary">Anuluj</a>
|
<a href="{{ url_for('admin.admin_calendar') }}" class="btn btn-secondary">Anuluj</a>
|
||||||
|
|||||||
@ -508,6 +508,21 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if event.attachment_filename %}
|
||||||
|
<div style="display: flex; align-items: center; gap: var(--spacing-md); padding: var(--spacing-lg); background: linear-gradient(135deg, #fef3c7, #fefce8); border: 1px solid #fde68a; border-radius: var(--radius-lg); margin-bottom: var(--spacing-xl);">
|
||||||
|
<svg width="24" height="24" fill="none" stroke="#92400e" stroke-width="2" viewBox="0 0 24 24" style="flex-shrink: 0;">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||||
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||||
|
</svg>
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<div style="font-weight: 600; color: #92400e;">Załącznik</div>
|
||||||
|
<a href="{{ url_for('static', filename=event.attachment_path.replace('static/', '')) }}" target="_blank" style="color: #b45309; text-decoration: underline;">{{ event.attachment_filename }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not event.is_past %}
|
{% if not event.is_past %}
|
||||||
{% if event.can_user_attend(current_user) %}
|
{% if event.can_user_attend(current_user) %}
|
||||||
<div class="rsvp-section">
|
<div class="rsvp-section">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user