- Add MembershipFee and MembershipFeeConfig models - Add /health endpoint for monitoring - Add Microsoft Fluent Design CSS - Update templates with new CSS structure - Add Announcement model - Update .gitignore to exclude analysis files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
195 lines
6.1 KiB
HTML
195 lines
6.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Zarzadzanie Ogloszeniami - Norda Biznes Hub{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.admin-header {
|
|
margin-bottom: var(--spacing-xl);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.admin-header h1 {
|
|
font-size: var(--font-size-3xl);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.section {
|
|
background: var(--surface);
|
|
padding: var(--spacing-xl);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
.announcements-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.announcements-table th,
|
|
.announcements-table td {
|
|
padding: var(--spacing-md);
|
|
text-align: left;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.announcements-table th {
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
text-transform: uppercase;
|
|
background: var(--background);
|
|
}
|
|
|
|
.announcements-table tr:hover {
|
|
background: var(--background);
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
border-radius: var(--radius-full);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.status-published { background: var(--success-bg); color: var(--success); }
|
|
.status-draft { background: var(--warning-bg); color: var(--warning); }
|
|
.status-expired { background: var(--surface-secondary); color: var(--text-secondary); }
|
|
|
|
.type-badge {
|
|
display: inline-block;
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
border-radius: var(--radius-sm);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 500;
|
|
background: var(--primary-bg);
|
|
color: var(--primary);
|
|
}
|
|
|
|
.type-fees { background: var(--warning-bg); color: var(--warning); }
|
|
.type-important { background: var(--error-bg); color: var(--error); }
|
|
.type-urgent { background: var(--error); color: white; }
|
|
|
|
.pinned-icon {
|
|
color: var(--warning);
|
|
margin-left: var(--spacing-xs);
|
|
}
|
|
|
|
.btn-small {
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.actions-cell {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: var(--spacing-2xl);
|
|
color: var(--text-secondary);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<div class="admin-header">
|
|
<h1>Zarzadzanie Ogloszeniami</h1>
|
|
<a href="{{ url_for('admin_announcements_new') }}" class="btn btn-primary">
|
|
+ Nowe ogloszenie
|
|
</a>
|
|
</div>
|
|
|
|
<div class="section">
|
|
{% if announcements %}
|
|
<table class="announcements-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Tytul</th>
|
|
<th>Typ</th>
|
|
<th>Status</th>
|
|
<th>Autor</th>
|
|
<th>Utworzono</th>
|
|
<th>Akcje</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for ann in announcements %}
|
|
<tr>
|
|
<td>
|
|
{{ ann.title }}
|
|
{% if ann.is_pinned %}<span class="pinned-icon" title="Przypiety">📌</span>{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="type-badge type-{{ ann.announcement_type }}">
|
|
{% if ann.announcement_type == 'general' %}Ogolne
|
|
{% elif ann.announcement_type == 'fees' %}Skladki
|
|
{% elif ann.announcement_type == 'event' %}Wydarzenie
|
|
{% elif ann.announcement_type == 'important' %}Wazne
|
|
{% elif ann.announcement_type == 'urgent' %}Pilne
|
|
{% else %}{{ ann.announcement_type }}
|
|
{% endif %}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if not ann.is_published %}
|
|
<span class="status-badge status-draft">Wersja robocza</span>
|
|
{% elif ann.expire_date and ann.expire_date < now %}
|
|
<span class="status-badge status-expired">Wygaslo</span>
|
|
{% else %}
|
|
<span class="status-badge status-published">Opublikowane</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ ann.author.name if ann.author else '-' }}</td>
|
|
<td>{{ ann.created_at.strftime('%Y-%m-%d %H:%M') if ann.created_at else '-' }}</td>
|
|
<td class="actions-cell">
|
|
<a href="{{ url_for('admin_announcements_edit', id=ann.id) }}" class="btn btn-secondary btn-small">
|
|
Edytuj
|
|
</a>
|
|
<button class="btn btn-error btn-small" onclick="deleteAnnouncement({{ ann.id }})">
|
|
Usun
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<p>Brak ogloszen. Utworz pierwsze ogloszenie klikajac przycisk powyzej.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
const now = new Date();
|
|
|
|
function deleteAnnouncement(id) {
|
|
if (!confirm('Czy na pewno chcesz usunac to ogloszenie?')) {
|
|
return;
|
|
}
|
|
|
|
fetch('/admin/announcements/' + id + '/delete', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': '{{ csrf_token() }}'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Blad: ' + data.error);
|
|
}
|
|
})
|
|
.catch(err => alert('Blad: ' + err));
|
|
}
|
|
{% endblock %}
|