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
- Add @rada_member_required decorator for access control - Add BoardDocument model for storing protocols and documents - Create document upload service (PDF, DOCX, DOC up to 50MB) - Add /rada/ blueprint with list, upload, download endpoints - Add "Rada" link in navigation (visible only for board members) - Add "Rada" badge and toggle button in admin user management - Create SQL migration to set up board_documents table and assign is_rada_member=True to 16 board members by email Storage: /data/board-docs/ (outside webroot for security) Access: is_rada_member=True OR role >= OFFICE_MANAGER Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
399 lines
11 KiB
HTML
399 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Strefa RADA - Norda Biznes Partner{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.board-header {
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.board-header h1 {
|
|
font-size: var(--font-size-3xl);
|
|
color: var(--text-primary);
|
|
margin-bottom: var(--spacing-sm);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.board-header p {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
.board-info-banner {
|
|
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
|
border: 1px solid #f59e0b;
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-xl);
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.board-info-banner svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
color: #d97706;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.board-info-banner .info-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.board-info-banner .info-title {
|
|
font-weight: 600;
|
|
color: #92400e;
|
|
margin-bottom: var(--spacing-xs);
|
|
}
|
|
|
|
.board-info-banner .info-text {
|
|
color: #a16207;
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.board-actions {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-lg);
|
|
flex-wrap: wrap;
|
|
gap: var(--spacing-md);
|
|
}
|
|
|
|
.board-filters {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.board-filters select {
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
background: white;
|
|
min-width: 120px;
|
|
}
|
|
|
|
.btn-upload {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 20px;
|
|
background: var(--primary);
|
|
color: white;
|
|
border-radius: var(--radius-md);
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-upload:hover {
|
|
background: var(--primary-dark);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-upload svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
.documents-table {
|
|
width: 100%;
|
|
background: white;
|
|
border-radius: var(--radius-lg);
|
|
border: 1px solid var(--border-color);
|
|
overflow: hidden;
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
.documents-table thead {
|
|
background: var(--bg-secondary);
|
|
}
|
|
|
|
.documents-table th {
|
|
padding: var(--spacing-md);
|
|
text-align: left;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.documents-table td {
|
|
padding: var(--spacing-md);
|
|
border-bottom: 1px solid var(--border-color);
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.documents-table tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.documents-table tbody tr:hover {
|
|
background: var(--bg-secondary);
|
|
}
|
|
|
|
.doc-title {
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.doc-type-badge {
|
|
display: inline-block;
|
|
padding: 4px 8px;
|
|
border-radius: var(--radius-sm);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.doc-type-badge.protocol {
|
|
background: #dbeafe;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
.doc-type-badge.minutes {
|
|
background: #d1fae5;
|
|
color: #059669;
|
|
}
|
|
|
|
.doc-type-badge.resolution {
|
|
background: #fef3c7;
|
|
color: #d97706;
|
|
}
|
|
|
|
.doc-type-badge.report {
|
|
background: #e0e7ff;
|
|
color: #4f46e5;
|
|
}
|
|
|
|
.doc-type-badge.other {
|
|
background: #f3f4f6;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.doc-meta {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.doc-actions {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.btn-download {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 6px 12px;
|
|
background: var(--success);
|
|
color: white;
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-download:hover {
|
|
background: #059669;
|
|
}
|
|
|
|
.btn-download svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.btn-delete {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 6px 10px;
|
|
background: transparent;
|
|
color: var(--danger);
|
|
border: 1px solid var(--danger);
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-delete:hover {
|
|
background: var(--danger);
|
|
color: white;
|
|
}
|
|
|
|
.btn-delete svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: var(--spacing-3xl);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.empty-state svg {
|
|
width: 64px;
|
|
height: 64px;
|
|
margin-bottom: var(--spacing-md);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.empty-state h3 {
|
|
margin-bottom: var(--spacing-sm);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.documents-table {
|
|
display: block;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.board-actions {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="board-header">
|
|
<h1>
|
|
<svg width="32" height="32" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="color: #f59e0b;">
|
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
|
<circle cx="9" cy="7" r="4"/>
|
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
|
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
|
</svg>
|
|
Strefa RADA
|
|
</h1>
|
|
<p>Dokumenty i protokoly z posiedzen Rady Izby NORDA</p>
|
|
</div>
|
|
|
|
<div class="board-info-banner">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
|
</svg>
|
|
<div class="info-content">
|
|
<div class="info-title">Strefa z ograniczonym dostepem</div>
|
|
<div class="info-text">
|
|
Ta sekcja jest dostepna wylacznie dla czlonkow Rady Izby NORDA.
|
|
Wszystkie dokumenty sa poufne i przeznaczone tylko do uzytku wewnetrznego.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="board-actions">
|
|
<form class="board-filters" method="GET" action="{{ url_for('board.index') }}">
|
|
<select name="year" onchange="this.form.submit()">
|
|
<option value="">Wszystkie lata</option>
|
|
{% for year in available_years %}
|
|
<option value="{{ year }}" {% if current_year == year %}selected{% endif %}>{{ year }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<select name="month" onchange="this.form.submit()">
|
|
<option value="">Wszystkie miesiace</option>
|
|
{% for m in range(1, 13) %}
|
|
<option value="{{ m }}" {% if current_month == m %}selected{% endif %}>
|
|
{{ ['Styczen', 'Luty', 'Marzec', 'Kwiecien', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpien', 'Wrzesien', 'Pazdziernik', 'Listopad', 'Grudzien'][m-1] }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<select name="type" onchange="this.form.submit()">
|
|
<option value="">Wszystkie typy</option>
|
|
{% for doc_type in document_types %}
|
|
<option value="{{ doc_type }}" {% if current_type == doc_type %}selected{% endif %}>{{ type_labels[doc_type] }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</form>
|
|
|
|
{% if can_upload %}
|
|
<a href="{{ url_for('board.upload') }}" class="btn-upload">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M12 4v16m8-8H4"/>
|
|
</svg>
|
|
Dodaj dokument
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if documents %}
|
|
<table class="documents-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Tytul</th>
|
|
<th>Typ</th>
|
|
<th>Data posiedzenia</th>
|
|
<th>Rozmiar</th>
|
|
<th>Dodano</th>
|
|
<th>Akcje</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for doc in documents %}
|
|
<tr>
|
|
<td>
|
|
<div class="doc-title">{{ doc.title }}</div>
|
|
{% if doc.description %}
|
|
<div class="doc-meta">{{ doc.description[:80] }}{% if doc.description|length > 80 %}...{% endif %}</div>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="doc-type-badge {{ doc.document_type }}">{{ doc.type_label }}</span>
|
|
</td>
|
|
<td class="doc-meta">{{ doc.meeting_date.strftime('%d.%m.%Y') }}</td>
|
|
<td class="doc-meta">{{ doc.size_display }}</td>
|
|
<td class="doc-meta">{{ doc.uploaded_at.strftime('%d.%m.%Y') }}</td>
|
|
<td>
|
|
<div class="doc-actions">
|
|
<a href="{{ url_for('board.download', doc_id=doc.id) }}" class="btn-download">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
|
|
</svg>
|
|
Pobierz
|
|
</a>
|
|
{% if can_upload %}
|
|
<form action="{{ url_for('board.delete', doc_id=doc.id) }}" method="POST" style="display: inline;"
|
|
onsubmit="return confirm('Czy na pewno chcesz usunac ten dokument?');">
|
|
<button type="submit" class="btn-delete">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
|
</svg>
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<svg fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
|
|
<path d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/>
|
|
</svg>
|
|
<h3>Brak dokumentow</h3>
|
|
<p>Nie znaleziono dokumentow spelniajacych kryteria wyszukiwania.</p>
|
|
{% if can_upload %}
|
|
<a href="{{ url_for('board.upload') }}" class="btn-upload" style="margin-top: var(--spacing-md);">
|
|
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M12 4v16m8-8H4"/>
|
|
</svg>
|
|
Dodaj pierwszy dokument
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|