nordabiz/templates/announcements/detail.html
Maciej Pienczyn e14d95394d feat(announcements): System ogłoszeń i aktualności dla członków
- Model Announcement z kategoriami, statusami, slugami URL
- Panel admina /admin/announcements (CRUD, filtry, AJAX)
- Strona /ogloszenia tylko dla zalogowanych członków
- Szczegóły ogłoszenia /ogloszenia/<slug>
- Migracja SQL rozszerzająca istniejącą tabelę
- Testowe ogłoszenia: ARP baza noclegowa, Tytani Przedsiębiorczości
- Pliki PDF regulaminu i harmonogramu konkursu Tytani

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 22:10:54 +01:00

376 lines
11 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ announcement.title }} - Aktualnosci - Norda Biznes Hub{% endblock %}
{% block meta_description %}{{ announcement.excerpt or announcement.content|striptags|truncate(160) }}{% endblock %}
{% block extra_css %}
<style>
.back-link {
display: inline-flex;
align-items: center;
gap: var(--spacing-xs);
color: var(--text-secondary);
text-decoration: none;
font-size: var(--font-size-sm);
margin-bottom: var(--spacing-lg);
}
.back-link:hover {
color: var(--primary);
}
.announcement-layout {
display: grid;
grid-template-columns: 1fr 300px;
gap: var(--spacing-2xl);
}
@media (max-width: 992px) {
.announcement-layout {
grid-template-columns: 1fr;
}
}
.announcement-main {
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
overflow: hidden;
}
.announcement-header {
padding: var(--spacing-xl);
border-bottom: 1px solid var(--border);
}
.announcement-meta {
display: flex;
align-items: center;
gap: var(--spacing-md);
margin-bottom: var(--spacing-md);
flex-wrap: wrap;
}
.category-badge {
display: inline-block;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--radius-sm);
font-size: var(--font-size-sm);
font-weight: 600;
background: var(--primary-bg);
color: var(--primary);
}
.category-event { background: #e0f2fe; color: #0369a1; }
.category-opportunity { background: #dcfce7; color: #15803d; }
.category-member_news { background: #fef3c7; color: #b45309; }
.category-partnership { background: #f3e8ff; color: #7c3aed; }
.meta-date {
color: var(--text-secondary);
font-size: var(--font-size-sm);
}
.meta-views {
color: var(--text-muted);
font-size: var(--font-size-sm);
}
.announcement-title {
font-size: var(--font-size-3xl);
font-weight: 700;
color: var(--text-primary);
line-height: 1.2;
margin-bottom: var(--spacing-md);
}
.announcement-excerpt {
font-size: var(--font-size-lg);
color: var(--text-secondary);
line-height: 1.5;
}
.announcement-image {
width: 100%;
max-height: 400px;
object-fit: cover;
}
.announcement-content {
padding: var(--spacing-xl);
}
.announcement-content p {
margin-bottom: var(--spacing-md);
line-height: 1.8;
}
.announcement-content h3 {
margin-top: var(--spacing-xl);
margin-bottom: var(--spacing-md);
font-size: var(--font-size-xl);
color: var(--text-primary);
}
.announcement-content ul, .announcement-content ol {
margin-bottom: var(--spacing-md);
padding-left: var(--spacing-xl);
}
.announcement-content li {
margin-bottom: var(--spacing-sm);
line-height: 1.6;
}
.announcement-content a {
color: var(--primary);
}
.announcement-content a:hover {
text-decoration: underline;
}
.external-link-box {
background: var(--primary-bg);
border: 1px solid var(--primary);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
margin: var(--spacing-xl) 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-md);
flex-wrap: wrap;
}
.external-link-box .link-text {
font-weight: 500;
color: var(--text-primary);
}
.external-link-box .btn {
white-space: nowrap;
}
.sidebar {
position: sticky;
top: var(--spacing-xl);
}
.sidebar-section {
background: var(--surface);
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
.sidebar-title {
font-size: var(--font-size-lg);
font-weight: 600;
color: var(--text-primary);
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-sm);
border-bottom: 2px solid var(--primary);
}
.other-announcement {
padding: var(--spacing-md) 0;
border-bottom: 1px solid var(--border);
}
.other-announcement:last-child {
border-bottom: none;
padding-bottom: 0;
}
.other-announcement:first-child {
padding-top: 0;
}
.other-announcement a {
color: var(--text-primary);
text-decoration: none;
font-weight: 500;
line-height: 1.4;
display: block;
}
.other-announcement a:hover {
color: var(--primary);
}
.other-announcement .date {
font-size: var(--font-size-xs);
color: var(--text-muted);
margin-top: var(--spacing-xs);
}
.share-buttons {
display: flex;
gap: var(--spacing-sm);
}
.share-btn {
width: 40px;
height: 40px;
border-radius: var(--radius);
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
font-size: 1.2em;
transition: transform 0.2s ease;
}
.share-btn:hover {
transform: scale(1.1);
}
.share-btn.facebook { background: #1877f2; color: white; }
.share-btn.linkedin { background: #0a66c2; color: white; }
.share-btn.twitter { background: #1da1f2; color: white; }
.share-btn.copy { background: var(--surface-secondary); color: var(--text-primary); border: 1px solid var(--border); }
.pinned-notice {
background: linear-gradient(135deg, var(--primary-bg) 0%, var(--surface) 100%);
border: 1px solid var(--primary);
border-radius: var(--radius);
padding: var(--spacing-sm) var(--spacing-md);
margin-bottom: var(--spacing-lg);
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-size: var(--font-size-sm);
color: var(--primary);
font-weight: 500;
}
</style>
{% endblock %}
{% block content %}
<div class="container">
<a href="{{ url_for('announcements_list') }}" class="back-link">
&larr; Powrot do listy ogloszen
</a>
{% if announcement.is_pinned %}
<div class="pinned-notice">
&#128204; To ogloszenie jest przypiete i wyswietla sie na gorze listy
</div>
{% endif %}
<div class="announcement-layout">
<!-- Main content -->
<article class="announcement-main">
<div class="announcement-header">
<div class="announcement-meta">
<span class="category-badge category-{{ announcement.category }}">
{{ category_labels.get(announcement.category, announcement.category) }}
</span>
<span class="meta-date">
{{ announcement.published_at.strftime('%d %B %Y') if announcement.published_at else '' }}
</span>
<span class="meta-views">
&#128065; {{ announcement.views_count or 0 }} wyswietlen
</span>
</div>
<h1 class="announcement-title">{{ announcement.title }}</h1>
{% if announcement.excerpt %}
<p class="announcement-excerpt">{{ announcement.excerpt }}</p>
{% endif %}
</div>
{% if announcement.image_url %}
<img src="{{ announcement.image_url }}" alt="{{ announcement.title }}" class="announcement-image"
onerror="this.style.display='none'">
{% endif %}
<div class="announcement-content">
{{ announcement.content|safe }}
{% if announcement.external_link %}
<div class="external-link-box">
<div class="link-text">
&#127760; Wiecej informacji znajdziesz na zewnetrznej stronie
</div>
<a href="{{ announcement.external_link }}" target="_blank" rel="noopener noreferrer" class="btn btn-primary">
Przejdz do strony &rarr;
</a>
</div>
{% endif %}
</div>
</article>
<!-- Sidebar -->
<aside class="sidebar">
<!-- Share -->
<div class="sidebar-section">
<h3 class="sidebar-title">Udostepnij</h3>
<div class="share-buttons">
<a href="https://www.facebook.com/sharer/sharer.php?u={{ request.url|urlencode }}"
target="_blank" rel="noopener" class="share-btn facebook" title="Udostepnij na Facebooku">
f
</a>
<a href="https://www.linkedin.com/sharing/share-offsite/?url={{ request.url|urlencode }}"
target="_blank" rel="noopener" class="share-btn linkedin" title="Udostepnij na LinkedIn">
in
</a>
<a href="https://twitter.com/intent/tweet?url={{ request.url|urlencode }}&text={{ announcement.title|urlencode }}"
target="_blank" rel="noopener" class="share-btn twitter" title="Udostepnij na Twitterze">
X
</a>
<button class="share-btn copy" title="Kopiuj link" onclick="copyLink()">
&#128279;
</button>
</div>
</div>
<!-- Other announcements -->
{% if other_announcements %}
<div class="sidebar-section">
<h3 class="sidebar-title">Inne ogloszenia</h3>
{% for other in other_announcements %}
<div class="other-announcement">
<a href="{{ url_for('announcement_detail', slug=other.slug) }}">
{{ other.title }}
</a>
<div class="date">
{{ other.published_at.strftime('%d.%m.%Y') if other.published_at else '' }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
<!-- Back to list -->
<div class="sidebar-section" style="text-align: center;">
<a href="{{ url_for('announcements_list') }}" class="btn btn-secondary" style="width: 100%;">
&larr; Wszystkie ogloszenia
</a>
</div>
</aside>
</div>
</div>
{% endblock %}
{% block extra_js %}
function copyLink() {
navigator.clipboard.writeText(window.location.href).then(function() {
const btn = document.querySelector('.share-btn.copy');
const originalText = btn.innerHTML;
btn.innerHTML = '&#10003;';
btn.style.background = 'var(--success-bg)';
btn.style.color = 'var(--success)';
setTimeout(function() {
btn.innerHTML = originalText;
btn.style.background = '';
btn.style.color = '';
}, 2000);
});
}
{% endblock %}